我有带 .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 且不会更改,所以这意味着代码以某种方式更改了另一个临时场景,我不确定。
我最终将场景结构更改为 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;
}
您的问题出在您用来识别已单击的按钮/场景的机制中:
在
scene.PlayStopCommand = new RelayCommand(() => PlayStop(scene));
中,您有一个用于局部 scene
变量的闭包。
这意味着当执行命令时,使用 scene
的实际值(在那个时刻)。但在那一刻scene
不再存在:它在局部范围内适用于for循环迭代。
所以你需要找到另一种方法来找到被点击的确切按钮。 有一种简单的方法可以做到这一点。例如:
重构您的命令以接受字符串参数:
public RelayCommand<string> PlayStopCommand { get; set; }
将场景的
Name
属性作为CommandParameter传递:
Command="{Binding PlayStopCommand}"
CommandParameter="{Binding Name"
重构命令实现以接受名称作为参数(并从中识别单击的按钮):
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 集合是可观察的,但场景的属性不在您发布的代码中) .
编辑:最后一部分在您自己的答案中处理...