无论如何要设置“DataContext” ListView 列共享的行的?

发布于 2024-10-03 16:23:36 字数 2600 浏览 3 评论 0原文

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

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

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

我确信有人会告诉我公开 User 对象从数据对象本身,而不是使用一些使用用户 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
}

That's the best way I could think of to phrase my question, here is the scenario: I have a ListView bound to a collection of objects. Each of those objects has a property UserID which is just a reference ID to a User object. In my ListView I wish to display multiple properties from both the object and the User. To do this I have created a class that implements MultiValueConverter to serve as a lookup table for the user objects. So I use a multibinding which passes the value converter the UserID and a dictionary look up table which is exposed by the underlying ViewModel.

This all works fine and dandy except I am hoping there is a way I could set the DataContext or something of the 'row' that the ListView columns share. In this way I could change my value converter to just return a User object instead of specific properties of the user object. And then I could just bind to the properties of that DataContext. I don't want to create a new value converter for each User property I wish to expose. The only other way I can think of to do this is by passing property names to value converter and using reflection.

Any ideas? I realize that this DataContext I am dreaming of is the job of the dataobjects bound to the ListView's ItemsSource, but perhaps there is something else I could use too. Attached Properties seem to solve every WPF problem I have so I am betting the solution would have to do with using an AttachedProperty to create this 'datacontext'

I'm sure someone will tell me to expose the User object from the dataobjects themselves instead of using some backwards method of using user ids and lookup table, BUT, I am doing this for a reason. thanks.

<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>

The converter:

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
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

养猫人 2024-10-10 16:23:36

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

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


也就是说,我无法抗拒给出你不想听到的答案(即使它对你没有帮助,但可能对其他人有帮助)。这就是我真正建议您做的事情...

  1. 创建一个组合当前对象(假设它称为Foo)和PhoneUser<的类/代码>.

    公共类 FooPhoneUser
    {
        Foo Foo { 得到;放; }
        PhoneUser 用户 { 获取;放; }
    }
    

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

    var FooPhoneUsers = 
        从
            福斯中的 f
        加入
            f.UserId 上的 PhoneUsers 中的 pu 等于 pu.Id
        选择
            新的 FooPhoneUser { Foo = f, User = pu };
    
  3. GridViewColumn 中删除所有绑定标记,然后放置如下内容:

    
    

    
    

So, if I understand you correctly, you'd like your converter to just return an entire PhoneUser object, then have each column decide which property of PhoneUser to grab?

If you're really going to insist on this convoluted method, I think your reflection idea (pass the property name into the converter and use reflection to return the value) would be best.


That said, I can't resist giving the answer you didn't want to hear (even if it doesn't help you, it might help someone else). Here's what I'd really recommend you do...

  1. Create a class that combines your current object (say it's called Foo) and a PhoneUser.

    public class FooPhoneUser
    {
        Foo Foo { get; set; }
        PhoneUser User { get; set; }
    }
    
  2. Use LINQ to combine these two classes together:

    var FooPhoneUsers = 
        from
            f in Foos
        join
            pu in PhoneUsers on f.UserId equals pu.Id
        select
            new FooPhoneUser { Foo = f, User = pu };
    
  3. Get rid of all that binding markup from your GridViewColumn, and just put something like this:

    <TextBlock MinWidth="120" Text={Binding User.LastName} />
    

    or

    <TextBlock MinWidth="120" Text={Binding Foo.SomeOtherProp} />
    
晒暮凉 2024-10-10 16:23:36

如果您可以使用 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;
        }
    }
}

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

It would be much easier if you could populate your data object with PhoneUser, instead of just the ID, then you could do:

<DataTemplate>
  <StackPanel>
    <TextBlock MinWidth="120" Text="{Binding Path="User.FirstName}">
    </TextBlock>
    <TextBlock MinWidth="120" Text="{Binding Path="User.LastName}">
    </TextBlock>
  </StackPanel>
</DataTemplate>

The class structure would look something like this:

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;
}

If it does not suit you to retrieve all user data when the data object is first loaded, you could use a "Lazy Loading" strategy, like this:

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;
        }
    }
}

I believe you could do this without any changes to the structure of your code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文