使用 MVVM Communitytoolkit 使用 RelayCommand 更改初始化按钮列表的属性

问题描述 投票:0回答:2

我有带 .NETCore 7 的 WPF 应用程序,当它加载时我有视图和视图模型

我使用视图模型中的代码生成一个按钮列表,如下所示

 public partial class DataViewModel : ObservableObject, INavigationAware
    {
        private bool _isInitialized = false;

        [ObservableProperty]
        private ObservableCollection<Scene> _scenes;


        public void OnNavigatedTo()
        {
            if (!_isInitialized)
                InitializeViewModel();
        }

        public void OnNavigatedFrom()
        {
        }

        private void InitializeViewModel()
        {
            var scenesCollection = new ObservableCollection<Scene>();

            for (int i = 0; i < 20; i++)
            {
                string name = "Scene " + (i + 1);
                var scene = new Scene
                {
                    Name = name,
                    Color = new SolidColorBrush(Color.FromArgb(200, 231, 129, 4)),
                    Icon = SymbolRegular.Play48,
                    IsPlaying = false
                };
                scene.PlayStopCommand = new RelayCommand(() => PlayStop(scene));
                scenesCollection.Add(scene);
            }

            Scenes = scenesCollection;

            _isInitialized = true;
        }

        [RelayCommand]
        public void PlayStop(Scene scene)
        {
            var random = new Random();
            if (scene.IsPlaying)
            {
                scene.Icon = SymbolRegular.Play48;
                scene.IsPlaying = false;
            }
            else
            {
                scene.Icon = SymbolRegular.Pause48; // Change the icon to the desired value
                scene.IsPlaying = true;
            }
            scene.Color = new SolidColorBrush(Color.FromArgb(
                (byte)200,
                (byte)random.Next(0, 250),
                (byte)random.Next(0, 250),
                (byte)random.Next(0, 250)));
        }
    }

场景结构如下

 public struct Scene
    {
        public int ID { get; set; }

        public string Name { get; set; }

        public Brush Color { get; set; }

        public RelayCommand PlayStopCommand { get; set; }

        public SymbolRegular Icon { get; set; }

        public bool IsPlaying { get; set; }
    }

XMAL代码

<ui:VirtualizingItemsControl
            Foreground="{DynamicResource TextFillColorSecondaryBrush}"
            ItemsSource="{Binding ViewModel.Scenes, Mode=OneWay}"
            VirtualizingPanel.CacheLengthUnit="Item">
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="{x:Type models:Scene}">
                    <ui:Button
                        x:Name="buttin"
                        Width="600"
                        Height="50"
                        Margin="2"
                        Padding="0"
                        HorizontalAlignment="Center"
                        VerticalAlignment="Center"
                        Appearance="Secondary"
                        Background="{Binding Color, Mode=OneWay}"
                        Command="{Binding PlayStopCommand}"
                        Content="{Binding Name, Mode=TwoWay}"
                        FontFamily="Arial"
                        FontSize="25"
                        FontWeight="Bold"
                        Foreground="White"
                        Icon="{Binding Icon, Mode=TwoWay}"
                        IconFilled="True" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ui:VirtualizingItemsControl>

按钮确实正确初始化,但是,当我单击任何按钮时,没有任何变化。

我什至尝试使用断点,我发现每次调用 PlayStopCommand 场景时。IsPlaying 始终为 false 且不会更改,所以这意味着代码以某种方式更改了另一个临时场景,我不确定。

c# wpf observablecollection relaycommand community-toolkit-mvvm
2个回答
0
投票

我最终将场景结构更改为 ObservableObject

   public partial class Scene : ObservableObject
{
    [ObservableProperty]
    public int _iD;

    [ObservableProperty]
    public string? name;

    [ObservableProperty]
    public Brush? color;

    [ObservableProperty]
    public RelayCommand? playStopCommand;

    [ObservableProperty]
    public SymbolRegular icon;

    [ObservableProperty]
    public bool isPlaying;
}

0
投票

您的问题出在您用来识别已单击的按钮/场景的机制中:

scene.PlayStopCommand = new RelayCommand(() => PlayStop(scene));
中,您有一个用于局部
scene
变量的闭包。 这意味着当执行命令时,使用
scene
的实际值(在那个时刻)。但在那一刻
scene
不再存在:它在局部范围内适用于for循环迭代。

所以你需要找到另一种方法来找到被点击的确切按钮。 有一种简单的方法可以做到这一点。例如:

  1. 重构您的命令以接受字符串参数:

    public RelayCommand<string> PlayStopCommand { get; set; }

  2. 将场景的

    Name
    属性作为CommandParameter传递:

     Command="{Binding PlayStopCommand}"
     CommandParameter="{Binding Name"
    
  3. 重构命令实现以接受名称作为参数(并从中识别单击的按钮):

    scene.PlayStopCommand = new RelayCommand((name) => PlayStop(name));
    

采用适应的

PlayStop
方法:

   public void PlayStop(string name)
   {
      // find the right scene in the _scenes collection
      var scene = _scenes.First((s) => s.Name == name); 
      
      var random = new Random();
      if (scene.IsPlaying)
      ...
            

最后:如果您希望场景属性的任何更改反映在您的 UI 中,您需要将更改通知视图(您的 _scenes 集合是可观察的,但场景的属性不在您发布的代码中) .

编辑:最后一部分在您自己的答案中处理...

© www.soinside.com 2019 - 2024. All rights reserved.