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

时间:2019-02-22 10:02:31

标签: c# wpf xaml sidebar

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

现在我真正的问题是:我已将命令绑定到每个按钮,一开始并不复杂。现在,该命令将其自己的状态设置为NormalActive以进行测试。此列表中的第一项应从LiveActive设置为Live(这样,您将始终看到当前选中的项)。这就是问题所在:按钮可以设置自己的状态,因此当我单击按钮3时,按钮3的状态从“正常”设置为“正常活动”。但是没有发生的是从第一个按钮将LiveActive更改为Active。即使在更改前后将当前状态输出到控制台,它也会为两者返回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并将网格替换为:

<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的新类,并将其替换为:

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

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

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的新类,并将其替换为:

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. 添加名为RelayCommand的新类,并将其替换为:

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

如果现在按下底部的按钮,则该按钮应变为绿色,而顶部的按钮应切换为红色。

1 个答案:

答案 0 :(得分:1)

每次调用

ListViewModel.Instance都会返回ListViewModel类的新实例。它应该返回相同的实例:

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