开发者

Anyway to set 'DataContext' of a row shared by ListView Columns?

开发者 https://www.devze.com 2023-01-26 00:56 出处:网络
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 refer

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
}


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


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消