gpt4 book ai didi

在 MVVM 中使用多重绑定(bind)的 WPF 聚合命令

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

我正在研究在 MVVM 中使用多重绑定(bind)进行 Leonid 命令聚合的解决方案。 https://www.codeproject.com/Articles/990113/MultiBinding-for-WPF-Command-Combining?msg=5666640#xx5666640xx在菜单中使用此解决方案时,我遇到了问题。我添加了一个包含两个项目的简单菜单:item1 和 item2。如果选择了 menuitem item1,那么 menuitem item1 和 item2 都会触发,这不是我想要的。我不明白的另一个方面是 XAML 部分是否包含 <Button.Command>没有注释,则在选择 menuitem item1 时会触发所有四个 North、West、South 和 East 命令。按钮的多重绑定(bind)似乎并不严格绑定(bind)到按钮,并且可用于其他控件。有什么想法吗?

我知道 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;
};
}
}
}

最佳答案

I would just like to know why all commands in 'MenuItem` fire when only one is selected



这是因为您对 MultiCommandConverter 的实现有缺陷:

public object Convert( object[ ] value, Type targetType,
object parameter, CultureInfo culture )
{
_value.AddRange( value );
return new RelayCommand( GetCompoundExecute( ), GetCompoundCanExecute( ) );
}

每次要求转换器对象将输入绑定(bind)转换为新的 ICommand对象,它会将传递给它的多个值添加到其内部命令列表中,然后返回一个新的 RelayCommand()简单地调用引用这些命令的委托(delegate)的对象。 IE。因为 GetCompoundExecute() 返回的委托(delegate)实例和 GetCompoundCanExecute()捕获 _value字段,稍后对该字段引用的列表发生的更改会反射(reflect)在之前创建的委托(delegate)中。

然后,将此转换器创建为资源,而不指定 x:Shared=false。 .这意味着您使用转换器的每个地方都使用相同的对象。因此,当所有 XAML 都被解析时,您就拥有了一个转换器,它结合了用于您使用该转换器的所有位置的所有命令。

一种解决方法可能是继续具体 x:Shared=false在资源中。但恕我直言,这并不是一个真正的好方法。一方面,这意味着现在您的转换器中有一个陷阱,并且必须记住,每次将转换器放入资源字典时都需要指定它。另一方面,您在转换器中有另一个错误,每次 Convert() 时都会将值添加到列表中。方法被调用。这意味着如果您尝试绑定(bind)到可能会不时更新的值,列表将变得越来越长,并且永远不会消除旧值。

您可以通过在每次 Convert() 时重置列表来解决此问题。方法被调用,即调用 _value.Clear() .但我认为这仍然是一个设计缺陷。转换器应该转换。它本身不应该在结果中发挥作用,就像您的实现在这里所做的那样。

恕我直言,更可取的替代方案包括:
  • 写一个 CompoundRelayCommand()包含 GetCompoundExecute()GetCompoundCanExecute()功能,您每次创建 Convert() 的新实例方法被调用。
  • 聚合 Convert() 中的输入值通过创建链接输入命令的新多播委托(delegate),然后将该多播委托(delegate)传递给 RelayCommand() .

  • 这些方法中的任何一种都比通过修复“列表未清除”错误并使用 x:Shared="false" 来更新您现在拥有的内容要好得多。在资源字典中。

    关于在 MVVM 中使用多重绑定(bind)的 WPF 聚合命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58178368/

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