简体   繁体   English

通过MVVM Light RelayCommand绑定通用类型

[英]Binding generic type through MVVM Light RelayCommand

I am using Implicit Data Templates which are applied to the items of an ItemsControl: 我正在使用应用于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>

This is databound to my ViewModel like so: 像这样将数据绑定到我的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
        }
    }
}

How can I get the objects from CategoryAttributeVM2s that match the type of 'ca'? 如何从CategoryAttributeVM2s中获取与“ ca”类型匹配的对象? I think I am missing something elementary, but cannot see it. 我想我缺少基本的东西,但是看不到。

The generics approach would be nice but AFAIK the RelayCommand cannot be made generic in that way: 泛型方法会很好,但是不能以这种方式使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; }

Any ideas? 有任何想法吗?

I'm not 100% sure I understand your question, but why not use object in your RelayCommand and make the CanExecute() method return true only if the object can be cast to the your generic type? 我不是100%肯定我理解您的问题,但是为什么不在您的RelayCommand使用object并使CanExecute()方法仅在该对象可以转换为泛型类型的情况下才返回true?

ICommand MyCommand { get; set; }

MyCommand = new RelayCommand<object>(
    param => DoCopyToChildrenCommand<T, U>(param as T),
    param => param is T);

In the end, I added an overload of an abstract method on CategoryAttributeVM and CategoryAttributeVM<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;
    }
}

I could then use this in DoCopyToChildrenCommand: 然后,我可以在DoCopyToChildrenCommand中使用它:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM