动态加载类型的ListBox数据模板

3

我有一个样本MVVM WPF应用程序,我在为我的动态加载的模型创建DataTemplates时遇到问题。让我尝试解释一下:

我有以下简化的类作为我的Model的一部分,我正在动态加载:

public class Relationship
{
    public string Category { get; set; }
    public ParticipantsType Participants { get; set; }
}

public class ParticipantsType
{
    public ObservableCollection<ParticipantType> Participant { get; set; }
}

public class ParticipantType
{

}

public class EmployeeParticipant : ParticipantType
{
    public EmployeeIdentityType Employee { get; set; }
}

public class DepartmentParticipant : ParticipantType
{
    public DepartmentIdentityType Department { get; set; }
}

public class EmployeeIdentityType
{
    public string ID { get; set; }
}

public class DepartmentIdentityType
{
    public string ID { get; set; }
}

这是我的视图模型,我创建了一个通用对象Model属性来公开我的模型:

    public class MainViewModel : ViewModelBase<MainViewModel>
{
    public MainViewModel()
    {
        SetMockModel();
    }

    private void SetMockModel()
    {
        Relationship rel = new Relationship();
        rel.Category = "213";
        EmployeeParticipant emp = new EmployeeParticipant();
        emp.Employee = new EmployeeIdentityType();
        emp.Employee.ID = "222";
        DepartmentParticipant dep = new DepartmentParticipant();            
        dep.Department = new DepartmentIdentityType();
        dep.Department.ID = "444";
        rel.Participants = new ParticipantsType() { Participant = new ObservableCollection<ParticipantType>() };
        rel.Participants.Participant.Add(emp);
        rel.Participants.Participant.Add(dep);            
        Model = rel;
    }

    private object _Model;
    public object Model
    {
        get { return _Model; }
        set
        {
            _Model = value;
            NotifyPropertyChanged(m => m.Model);
        }
    }
}

接着我尝试创建一个ListBox来专门显示“Participants”集合:

<ListBox ItemsSource="{Binding Path=Model.Participants.Participant}">
<ListBox.ItemTemplate>
    <DataTemplate>
        <StackPanel>
            <Expander Header="IdentityFields">
            <!-- WHAT TO PUT HERE IF PARTICIPANTS HAVE DIFFERENT PROPERTY NAMES -->
            </Expander>
        </StackPanel>
    </DataTemplate>
</ListBox.ItemTemplate>

问题是:
1. 我不知道如何创建一个可以处理两种参与者类型的模板,在这种情况下,我可能会有EmployeeParticipant或DepartmentParticipant,因此,根据情况,数据绑定路径将被设置为Employee或Department属性。 2. 我考虑为每种类型创建一个DataTemplate(例如x:Type EmployeeParticipant),但问题在于我的模型类在运行时动态加载,因此VisualStudio会抱怨这些类型不存在于当前解决方案中。
那么如果我的具体类型在编译时未知,只有在运行时才知道,我该如何在ListBox中表示这些数据呢?
编辑:添加了我的测试ViewModel类

这里有一个相关的问题:https://dev59.com/KmXWa4cB1Zd3GeqPLUcs - Adolfo Perez
3个回答

2
您仍然可以为每个类型创建一个DataTemplate,但是不要使用DataType声明自动解析它们,而是可以创建一个DataTemplateSelector,其中包含每个模板的属性(从XAML中的StaticResource分配),该属性可以将传入的数据项转换为基类并检查属性或以其他方式确定在运行时要使用哪个模板。将该选择器分配给ListBox.ItemTemplateSelector,您将获得与DataType相似的行为。

在评估了所有不同的选项之后,我认为您的答案根据我的要求更好地完成了工作。我只是在我的ItemTemplateSelector中检查项目对象类型,并基于此返回一个DataTemplate。根据您的建议,我将根据最终方法更新问题。感谢您的帮助! - Adolfo Perez

0

那不是一个好的视图模型。你的视图模型应该以视图为中心,而不是以业务为中心。因此,创建一个可以从视觉角度处理所有四种情况的类,然后将你的业务类桥接到该视图模型上。


编辑:

根据您的代码进行工作:

<ListBox ItemsSource="{Binding Path=Model.Participants}">
<ListBox.ItemTemplate>
    <DataTemplate>
        <StackPanel>
            <Expander Header="IdentityFields">
                <TextBlock Text={Binding Id} />
                <TextBlock Text={Binding Name} />
            </Expander>
        </StackPanel>
    </DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

我改变了绑定,我想这是一个错误吗?

我会为参与者创建一个ViewModel:

public class Participant_VM : ViewModelBase
{

    private string _name = string.Empty;
    public string Name
    {
        get
        {
            return _name ;
        }

        set
        {
            if (_name == value)
            {
                return;
            }
            _name = value;
            RaisePropertyChanged(() => Name);
        }

    private string _id= string.Empty;
    public string Id
    {
        get
        {
            return _id;
        }

        set
        {
            if (_id== value)
            {
                return;
            }
            _id = value;
            RaisePropertyChanged(() => Id);
        }

    }
}

很高兴你提出了这个问题。我已经将整个ViewModel类添加到了问题中。 - Adolfo Perez
当你说“创建一个可以从视觉角度处理所有四种情况的类”时,你是指XAML视图吗,@Shlomo? - Adolfo Perez

0

请按以下方式修改ListBox

        <ListBox ItemsSource="{Binding Model.Participants.Participant}">
            <ListBox.Resources>
                <DataTemplate DataType="{x:Type loc:DepartmentParticipant}">
                    <Grid>
                        <TextBlock Text="{Binding Department.ID}"/>
                    </Grid>
                </DataTemplate>
                <DataTemplate DataType="{x:Type loc:EmployeeParticipant}">
                    <Grid>
                        <TextBlock Text="{Binding Employee.ID}"/>
                    </Grid>
                </DataTemplate>
            </ListBox.Resources>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <Expander Header="IdentityFields">
                            <!-- WHAT TO PUT HERE IF PARTICIPANTS HAVE DIFFERENT PROPERTY NAMES -->
                            <ContentPresenter Content="{Binding }"/>
                        </Expander>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

编辑: loc指的是DepartmentParticipantEmployeeParticipant所在的命名空间。希望您熟悉如何添加命名空间。


谢谢,就像我说的,我的模型类型是在运行时加载的,所以我无法在设计时使用这些类型。即使我在WPF项目中添加对我的模型dll的引用,它也会添加命名空间:xmlns:loc="clr-namespace:;assembly=PIM_SBP.TransactionPolicy",但仍然无法工作。我得到了:“类型引用找不到名为DepartmentParticipant的公共类型”的错误。 - Adolfo Perez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接