通过MVVM Light RelayCommand绑定泛型类型

本文关键字:绑定 泛型类型 RelayCommand Light MVVM 通过 | 更新日期: 2023-09-27 18:21:20

我使用的是应用于ItemsControl:项目的隐式数据模板

<ItemsControl ItemsSource="{Binding Path=CategoryAttributeVMs}"/>
<DataTemplate DataType="{x:Type my:CategoryAttributeDateFieldVM}">
    <DockPanel>
        <.....>
        <Button Command="{Binding Path=MainViewModel.CopyToChildrenCommand,
                                  Source={StaticResource Locator}}"
                CommandParameter="{Binding Mode=OneWay}">
    </DockPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type my:CategoryAttributeIntegerFieldVM}">
    <DockPanel>
        <.....>
        <Button Command="{Binding Path=MainViewModel.CopyToChildrenCommand,
                                  Source={StaticResource Locator}}"
                CommandParameter="{Binding Mode=OneWay}">
    </DockPanel>
</DataTemplate>

这是数据绑定到我的ViewModel,如下所示:

public abstract class CategoryAttributeVM
{
    protected CategoryAttributeVM(ICategoryAttribute categoryAttribute)
    {
        _categoryAttribute = categoryAttribute;
    }
    private readonly ICategoryAttribute _categoryAttribute;
    public string Name { get { return _categoryAttribute.Name; } }
    public object Value { get { return _categoryAttribute.Value; } }
}
public abstract class CategoryAttributeVM<T> : CategoryAttributeVM
{
    protected CategoryAttributeVM(ICategoryAttribute<T> categoryAttribute)
        : base(categoryAttribute) { _categoryAttribute = categoryAttribute; }
    private readonly ICategoryAttribute<T> _categoryAttribute;
    public new T Value
    {
        get { return _categoryAttribute.Value; }
        set { _categoryAttribute.Value = value; }
    }
}
public class CategoryAttributeDateFieldVM : CategoryAttributeVM<DateTime?>
{
    public CategoryAttributeDateFieldVM(ICategoryAttributeDateField categoryAttributeDateField)
        : base(categoryAttributeDateField) { }
}
public class CategoryAttributeIntegerFieldVM: CategoryAttributeVM<Int32?>
{
    public CategoryAttributeIntegerFieldVM(ICategoryAttributeIntegerField categoryAttributeIntegerField)
       : base(categoryAttributeIntegerField) { }
}
public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        CategoryAttributeVMs = new List<CategoryAttributeVM>( new CategoryAttributeVM[]
            {
                new CategoryAttributeDateFieldVM(new CategoryAttributeDateField("DateField", DateTime.Now)),
                new CategoryAttributeIntegerFieldVM(new CategoryAttributeIntegerField("IntField", 123))
            });
        CategoryAttributeVM2s = new List<CategoryAttributeVM>(new CategoryAttributeVM[]
            {
                new CategoryAttributeDateFieldVM(new CategoryAttributeDateField("DateField", null)),
                new CategoryAttributeIntegerFieldVM(new CategoryAttributeIntegerField("IntField", null))
            });
        CopyToChildrenCommand = new RelayCommand<CategoryAttributeVM>(DoCopyToChildrenCommand);
    }
    public IEnumerable<CategoryAttributeVM> CategoryAttributeVMs { get; private set; }
    private IEnumerable<CategoryAttributeVM> CategoryAttributeVM2s { get; private set; }
    // This an MVVM Light RelayCommand
    public RelayCommand<CategoryAttributeVM> CopyToChildrenCommand { get; private set; }
    private void DoCopyToChildrenCommand(CategoryAttributeVM ca)
    {
        foreach (var item in CategoryAttributeVM2s)
        {
            // How to copy the ca.Value to item.Value where ca and item are of the same type?
            if (item.GetType() == ca.GetType())
                item.Value = ca.Value;  // !! No worky because item.Value refers to the CategoryAttributeVM.Value, which is a readonly Object property
        }
    }
}

如何从CategoryAttributeVM2s中获取与"ca"类型匹配的对象?我想我错过了一些基本的东西,但看不见。

泛型方法会很好,但AFAIK RelayCommand不能以这种方式变得通用:

// This would be great
private void DoCopyToChildrenCommand<T, U>(T ca) where T : CategoryAttributeVM<U>
{
    foreach (var item in CategoryAttributeVM2s.OfType<T>())
        item.Value = ca.Value;  // item.Value is type U, which is perfect
}
// But this can't be, obviously
public RelayCommand<CategoryAttributeVM<U>> CopyToChildrenCommand<U> { get; private set; }

有什么想法吗?

通过MVVM Light RelayCommand绑定泛型类型

我不能100%确定我是否理解您的问题,但为什么不在RelayCommand中使用object,并使CanExecute()方法仅在对象可以强制转换为泛型类型时返回true?

ICommand MyCommand { get; set; }
MyCommand = new RelayCommand<object>(
    param => DoCopyToChildrenCommand<T, U>(param as T),
    param => param is T);

最后,我在CategoryAttributeVMCategoryAttributeVM<T>:上添加了一个抽象方法的重载

public abstract class CategoryAttributeVM 
{ 
    <...>
    public abstract void UpdateValueFrom(CategoryAttributeVM sourceCategoryAttributeVM);
} 
public abstract class CategoryAttributeVM<T> : CategoryAttributeVM 
{
    <...>
    public override void UpdateValueFrom(CategoryAttributeVM sourceCategoryAttributeVM)
    {
        if (GetType() == sourceCategoryAttributeVM.GetType())
            Value = (T)sourceCategoryAttributeVM.Value;
    }
}

然后我可以在DoCopyToChildrenCommand:中使用这个

private void DoCopyToChildrenCommand(CategoryAttributeVM ca)
{
    foreach (var attribute in CategoryAttributeVM2s)
        attribute.UpdateValueFrom(ca);
}