简体   繁体   中英

WPF Binding to a child collection

I have the following entities:

public class User
{
    public User()
    {
        Roles = new ObservableCollection<Role>();
    }

    public int UserId { get; set; }
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }

    public virtual ICollection<Role> Roles { get; set; }
}

public class Role
{
    public int RoleId { get; set; }
    public string Name { get; set; }

    public virtual User User { get; set; }
}

With this ViewModel:

public class UserManagerViewModel : ObservableObject
{
    public ObservableCollection<Role> AllRoles { get; set; }
    public UserViewModel()
    {
        AllRoles = new ObservableCollection<Role>(RoleRepository.GetAll());
    }

    private User _selectedUser;
    public User SelectedUser
    {
        get { return _selectedUser; }
        set
        {
            if (_selectedUser != value)
            {
                _selectedUser = value;
                RaisePropertyChanged();
            }
        }
    }

    ...
}

I want to display the SelectedUser roles in the following way(or anything similar):

<Window.DataContext>
    <vm:UserManagerViewModel/>
</Window.DataContext>
<ListBox ItemsSource="{Binding AllRoles}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <CheckBox Content="{Binding Name}" IsChecked="{Binding ???}"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
 </ListBox>

To what do I need to set the IsChecked property of the CheckBox so that it will represent the SelectedUser roles?

Assuming that you want to "check" roles that SelectedUser has.

First we answer the question, "What data does this depend on?" The answer is pretty simple, it depends on the role itself, so we write:

<CheckBox Content="{Binding Name}" IsChecked="{Binding .}"/>

Now clearly, that isn't a bool; so we will need to write a converter for it that checks against a collection. We could do a MultiValueConverter here (as in @Moji's answer), but it's probably easier to expose the collection via a Dependency Property, and bind when creating the converter.

<Window.Resources>
   <local:CollectionContainsConverter Collection="{Binding SelectedUser.Roles}"/>
</Window.Resources>

<CheckBox Content="{Binding Name}" IsChecked="{Binding Path=., Converter={StaticResource CollectionContainsConverter}"/>

And the converter:

public class CollectionContainsConverter : IValueConverter
{
    public IEnumerable<object> Collection { get; set; } //This is actually a DP

    public object Convert(...)
    {
        return Collection.Contains(value);
        // or possibly, to allow for the Object.Equals override
        return Collection.Any(o => o.Equals(value));
    }

    public object ConvertBack(...)
    {
         return Binding.DoNothing;
    }
}

Having not tested this, you may need to use the second return so it doesn't compare references, and utilize Object.Equals (or another comparator of your choice) to figure out if the item is in the list.

you can use MultipleBinding and MultipleValueVonverter as below

  <Grid>
        <Grid.Resources>
            <local:RoleValueConverter x:Key="converter"></local:RoleValueConverter>
        </Grid.Resources>
        <ListBox ItemsSource="{Binding AllRoles}" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <CheckBox Content="{Binding Name}" >
                        <CheckBox.IsChecked>
                            <MultiBinding Converter="{StaticResource converter}">
                                <Binding Path="."></Binding>
                                <Binding  RelativeSource="{RelativeSource AncestorType={x:Type ListBox}}"  Path="DataContext"></Binding>
                            </MultiBinding>
                        </CheckBox.IsChecked>
                    </CheckBox>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
 public class RoleValueConverter : IMultiValueConverter
    {

        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
           //values[0] is Role object
    //value[1] is UserManagerViewModel 
   // then you can see if Selected user had Role object return true else return false 
        }

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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