项目命令无法编辑列表中的其他项目

Item command cannot edit another item in the list

我有一个侧边栏(在 C# WPF 程序中)应该显示 4 个 "different" 按钮(它们实际上是 2 种不同的样式,它们都有另一种用于活动状态的样式)。侧边栏由一个 ItemsControl 组成。我现在设法创建了一个列表,其中根据枚举值使用了正确的样式(如下所示)。这里有一个小问题:我可以这样做吗,或者我应该重写它,如果是这样,如何构建这样的东西?关键字或我必须看的东西对我来说已经足够了。

我现在真正的问题是:我已经为每个按钮绑定了一个命令,一开始并不复杂。该命令现在将其自身状态设置为 NormalActive 以用于测试目的。此列表中的第一项应从 LiveActive 设置为 Live(以便您始终看到您所知道的当前所选项目)。问题来了:按钮可以设置自己的状态,所以当我点击按钮 3 时,按钮 3 的状态从 Normal 设置为 NormalActive。但是没有发生的是第一个按钮从 LiveActive 到 Active 的变化。即使我在更改前后将当前状态输出到控制台,它 returns LiveActive 都适用。如果由于某种原因我不在 UI 线程中,我也尝试将整个事情调用到调度程序中,但它没有用。所以按钮可以设置自己的状态,但不能设置另一个按钮的状态。但我没有收到错误消息或任何信息。 属性 的 setter 方法也被调用,它只是不改变它。可能是什么原因?

PluginListControl:

<Grid DataContext="{x:Static local:PluginListDesignModel.Instance}">
    <ScrollViewer VerticalScrollBarVisibility="Auto">
        <ItemsControl ItemsSource="{Binding Items}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <local:PluginListItemControl />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>
</Grid>

PluginListItemControl:

<UserControl.Resources>
    <DataTemplate x:Key="PluginTile" DataType="{x:Type local:PluginListItemViewModel}">
        <Button Style="{StaticResource PluginTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
    </DataTemplate>

    <DataTemplate x:Key="PluginActiveTile" DataType="{x:Type local:PluginListItemViewModel}">
        <Button Style="{StaticResource PluginActiveTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
    </DataTemplate>

    <DataTemplate x:Key="PluginLiveTile" DataType="{x:Type local:PluginListItemViewModel}">
        <Button Style="{StaticResource PluginLiveTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
    </DataTemplate>

    <DataTemplate x:Key="PluginActiveLiveTile" DataType="{x:Type local:PluginListItemViewModel}">
        <Button Style="{StaticResource PluginActiveLiveTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
    </DataTemplate>
</UserControl.Resources>
<ContentControl d:DataContext="{x:Static local:PluginListItemDesignModel.Instance}">

    <ContentControl.Style>
        <Style TargetType="{x:Type ContentControl}">
            <Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding State}" Value="0">
                    <Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding State}" Value="1">
                    <Setter Property="ContentTemplate" Value="{StaticResource PluginActiveTile}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding State}" Value="2">
                    <Setter Property="ContentTemplate" Value="{StaticResource PluginLiveTile}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding State}" Value="3">
                    <Setter Property="ContentTemplate" Value="{StaticResource PluginActiveLiveTile}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>

</ContentControl>

PluginListItemViewModel:(每个列表项的 ViewModel)

public class PluginListItemViewModel : BaseViewModel
{
    public string Name { get; set; }
    public PluginTileStates State { get; set; }
    public ICommand SetStateCommand { get; set; }

    #region Constructor

    /// <summary>
    /// Default constructor
    /// </summary>
    public PluginListItemViewModel()
    {
        SetStateCommand = new RelayCommand(() => SetState());
    }

    #endregion

    private void SetState()
    {
        PluginListDesignModel.Instance.Items[0].State = PluginTileStates.Live;
        State = PluginTileStates.NormalActive;
    }
}

重现步骤:

  1. 创建一个新的 WPF 项目,.NET Framework 4.6.1(Visual Studio 2017)。
  2. 用以下内容替换 MainWindow 中的网格:

<Grid DataContext="{x:Static local:ListViewModel.Instance}">
    <ScrollViewer VerticalScrollBarVisibility="Auto">
        <ItemsControl ItemsSource="{Binding Items}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <local:ListItemControl />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>
</Grid>
  1. 添加一个名为 ListItemControl 的新用户控件并将网格替换为:

<UserControl.Resources>
    <Style x:Key="Tile" TargetType="{x:Type Button}">
        <Setter Property="Background" Value="Red" />
    </Style>

    <Style x:Key="ActiveTile" TargetType="{x:Type Button}">
        <Setter Property="Background" Value="Green" />
    </Style>

    <DataTemplate x:Key="PluginTile" DataType="{x:Type local:ListItemViewModel}">
        <Button Width="100" Height="60" Style="{StaticResource Tile}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
    </DataTemplate>

    <DataTemplate x:Key="PluginActiveTile" DataType="{x:Type local:ListItemViewModel}">
        <Button Width="100" Height="60" Style="{StaticResource ActiveTile}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
    </DataTemplate>
</UserControl.Resources>
<ContentControl>
    <ContentControl.Style>
        <Style TargetType="{x:Type ContentControl}">
            <Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding State}" Value="0">
                    <Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding State}" Value="1">
                    <Setter Property="ContentTemplate" Value="{StaticResource PluginActiveTile}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>
</ContentControl>
  1. 添加一个名为 BaseViewModel 的新 class 并将 class 替换为:

public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };

    public void OnPropertyChanged(string name)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
}
  1. 添加名为 ListItemViewModel 的新 class 并将 class 替换为:

public enum TileStates
{
    Normal = 0,
    Active = 1
}

public class ListItemViewModel : BaseViewModel
{
    public TileStates State { get; set; }

    public ICommand SetStateCommand { get; set; }

    public ListItemViewModel()
    {
        SetStateCommand = new RelayCommand(() =>
          {
              ListViewModel.Instance.Items[0].State = TileStates.Normal;
              State = TileStates.Active;
          });
    }
}
  1. 添加名为 ListViewModel 的新 class 并将 class 替换为:

public class ListViewModel : BaseViewModel
{
    public static ListViewModel Instance => new ListViewModel();

    public List<ListItemViewModel> Items { get; set; } = new List<ListItemViewModel>
    {
        new ListItemViewModel
        {
            State = TileStates.Active
        },
        new ListItemViewModel
        {
            State = TileStates.Normal
        }
    };
}
  1. 添加新的 class 调用 RelayCommand 并将 class 替换为:

public class RelayCommand : ICommand
{
    private Action mAction;

    public event EventHandler CanExecuteChanged = (sender, e) => { };

    public RelayCommand(Action action)
    {
        mAction = action;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        mAction();
    }
}
  1. 安装 NuGet 包:"Fody v4.0.2" 和 "PropertyChanged.Fody v2.6.0"(您可能需要在安装后重新启动 Visual Studio

如果您现在按下底部的按钮,它应该变成绿色,而顶部的按钮应该变成红色。

ListViewModel.Instance return 每次调用 ListViewModel class 的新实例。它应该 return 相同的实例:

public static ListViewModel Instance { get; } = new ListViewModel();