C# 是否仍要设置ListView列共享的行的“DataContext”?

C# 是否仍要设置ListView列共享的行的“DataContext”?,c#,wpf,listview,datacontext,C#,Wpf,Listview,Datacontext,这是我能想到的表达我的问题的最好方式,下面是一个场景:我有一个绑定到对象集合的ListView。每个对象都有一个属性UserID,它只是用户对象的引用ID。在我的ListView中,我希望显示对象和用户的多个属性。为此,我创建了一个实现MultiValueConverter的类,作为用户对象的查找表。因此,我使用了一个多重绑定,它向值转换器传递UserID和一个字典查找表,该表由底层ViewModel公开 除了我希望有一种方法可以设置DataContext或ListView列共享的“行”的某些内

这是我能想到的表达我的问题的最好方式,下面是一个场景:我有一个绑定到对象集合的ListView。每个对象都有一个属性UserID,它只是用户对象的引用ID。在我的ListView中,我希望显示对象和用户的多个属性。为此,我创建了一个实现MultiValueConverter的类,作为用户对象的查找表。因此,我使用了一个多重绑定,它向值转换器传递UserID和一个字典查找表,该表由底层ViewModel公开

除了我希望有一种方法可以设置DataContext或ListView列共享的“行”的某些内容之外,所有这些都可以很好地工作。通过这种方式,我可以将我的值转换器更改为只返回一个用户对象,而不是用户对象的特定属性。然后我可以绑定到DataContext的属性。我不想为我想要公开的每个用户属性创建一个新的值转换器。我能想到的唯一其他方法是将属性名传递给值转换器并使用反射

有什么想法吗?我意识到我梦寐以求的这个DataContext是绑定到ListView的ItemsSource的dataobjects的工作,但也许我还可以使用其他东西。附加属性似乎解决了我遇到的每个WPF问题,所以我打赌解决方案必须使用AttachedProperty来创建这个“datacontext”

我相信有人会告诉我从数据对象本身公开用户对象,而不是使用一些向后的方法使用用户ID和查找表,但是,我这样做是有原因的。谢谢

<ListView.View>
    <GridView>
        <GridViewColumn>
            <GridViewColumn.Header>User</GridViewColumn.Header>
            <GridViewColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock MinWidth="120">
                        <TextBlock.Text>
                            <MultiBinding Converter="{StaticResource UserIDConverter}">
                                <Binding Path="UserID" />
                                <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=UserControl}" Path="DataContext.Users"/>
                            </MultiBinding>
                        </TextBlock.Text>
                    </TextBlock>
                </DataTemplate>
            </GridViewColumn.CellTemplate>
        </GridViewColumn>
    </GridView>
</ListView.View>
转换器:

public class UserIDConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object parameter,
                          System.Globalization.CultureInfo culture)
    {
        int userId = (int)values[0];
        IDictionary<int, PhoneUser> table = values[1] as IDictionary<int, PhoneUser>;
        if (table.ContainsKey(userId))
        {
            PhoneUser user = table[userId];
            return user.LastName;
            //I'd like to just return user !!
        }

        return null;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter,
                                System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

如果您可以使用PhoneUser(而不仅仅是ID)填充数据对象,那么您可以执行以下操作:

<DataTemplate>
  <StackPanel>
    <TextBlock MinWidth="120" Text="{Binding Path="User.FirstName}">
    </TextBlock>
    <TextBlock MinWidth="120" Text="{Binding Path="User.LastName}">
    </TextBlock>
  </StackPanel>
</DataTemplate>
如果不适合在首次加载数据对象时检索所有用户数据,则可以使用延迟加载策略,如下所示:

public class myDataObject //The data object you already have.
{
    public string value1;
    public string value2;
    public PhoneUser User; //currently you have "UserID" here.
}

public class PhoneUser
{
    public string FirstName;
    public string LastName;
}
public class myDataObject //The data object you already have.
{
    public string UserID;
    public string value2;
    private PhoneUser _User; 
    public PhoneUser User
    {
        get
        {
            if(_User==null)
                _User = getUserFromDatabase(UserID);
            return _User;
        }
    }
}
<TextBlock MinWidth="120" Text={Binding User.LastName} />

我相信您可以在不更改代码结构的情况下执行此操作。

因此,如果我理解正确,您希望转换器只返回整个PhoneUser对象,然后让每列决定要获取PhoneUser的哪个属性

如果您真的要坚持使用这种复杂的方法,我认为您的反射想法最好将属性名传递到转换器中,并使用反射返回值

也就是说,我忍不住给出你不想听的答案,即使它对你没有帮助,也可能对其他人有帮助。这是我真正建议你做的

创建一个类,将当前对象(比如称为Foo)和一个PhoneUser)组合在一起

使用LINQ将这两个类组合在一起:

var FooPhoneUsers = 
    from
        f in Foos
    join
        pu in PhoneUsers on f.UserId equals pu.Id
    select
        new FooPhoneUser { Foo = f, User = pu };
摆脱GridViewColumn中的所有绑定标记,只需放置如下内容:

public class myDataObject //The data object you already have.
{
    public string value1;
    public string value2;
    public PhoneUser User; //currently you have "UserID" here.
}

public class PhoneUser
{
    public string FirstName;
    public string LastName;
}
public class myDataObject //The data object you already have.
{
    public string UserID;
    public string value2;
    private PhoneUser _User; 
    public PhoneUser User
    {
        get
        {
            if(_User==null)
                _User = getUserFromDatabase(UserID);
            return _User;
        }
    }
}
<TextBlock MinWidth="120" Text={Binding User.LastName} />


谢谢,但正如问题中所述,我不想在数据对象类中使用对用户对象实例的引用。谢谢你这么想对不起,我错过了。你能详细说明你不想这样做的原因吗?如果我知道你的要求是基于什么,我可能会更好地给你一个好答案。我认为必须有一个比你正在尝试的更好的策略。“更好的策略”,我相信你是对的。我只是要修改我的其他层,这样我就可以在我的数据对象中使用一个用户对象。我想我只是被困在试图使这项工作,而不是将项目作为一个整体来考虑。@J-我已经修改了我的答案,加入了更多关于延迟加载的细节-这项工作的性能和有效性应该与您的问题所建议的相同。重构您的代码是一个好主意,但这可能会让您摆脱麻烦:在这一点上,我认为我的非UI层只是构思不周,所以我将对它们进行返工,以便在我的数据对象中使用用户对象引用。