简体   繁体   English

MVVM中的ListView SelectedItems数据绑定实现

[英]ListView SelectedItems DataBinding Implementation in MVVM

I am having one problem to bind SelectedItems of ListView control, in order to solve it i have extended ListView control, ListViewExtended to expose SelectedItemsList using the reference from this post but two way binding is not working to get the SelectedItems in the model. 我有绑定ListView控件的SelectedItems,为了解决它,我已经扩展ListView控件一个问题, ListViewExtended使用此参考揭露SelectedItemsList ,但双向绑定是不工作得到模型中的SelectedItems。

Kindly helps to point out the issue in this demo source. 请帮助指出此演示源中的问题。

Custom Control: 自定义控件:

public sealed class ListViewExtended : ListView
{
    public ListViewExtended()
    {
        this.SelectionChanged += ListViewExtended_SelectionChanged;
    }

    void ListViewExtended_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        this.SelectedItemsList = this.SelectedItems;
    }

    #region SelectedItemsList

    public IEnumerable SelectedItemsList
    {
        get { return (IEnumerable)GetValue(SelectedItemsListProperty); }
        set { SetValue(SelectedItemsListProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemsListProperty =
            DependencyProperty.Register("SelectedItemsList",
            typeof(IEnumerable),
            typeof(ListViewExtended),
            new FrameworkPropertyMetadata(null));

    #endregion
}

Model: 模型:

public class Member
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public class Family : BindableBase
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; OnPropertyChanged("Name"); }
    }

    private IList<Member> _selectedMembers;
    public IList<Member> SelectedMembers
    {
        get { return _selectedMembers; }
        set { _selectedMembers = value; OnPropertyChanged("SelectedMembers"); }
    }

    public override string ToString()
    {
        return Name;
    }
}

ViewModel: ViewModel:

public class ListViewSelectedItemsExtendedViewModel : BindableBase
{
    Dictionary<string, IEnumerable> _repository = new Dictionary<string, IEnumerable>();

    public ListViewSelectedItemsExtendedViewModel()
    {
        var families = new List<Family>();
        families.Add(new Family() { Name = "family 1" });
        families.Add(new Family() { Name = "family 2" });
        Families = families;

        var items = new List<Member>();
        items.Add(new Member() { Name = "John", Age = 30 });
        items.Add(new Member() { Name = "Chapel", Age = 50 });
        items.Add(new Member() { Name = "Max", Age = 46 });
        _repository.Add(families[0].Name, items);

        items = new List<Member>();
        items.Add(new Member() { Name = "Warner", Age = 28 });
        items.Add(new Member() { Name = "Peter", Age = 36 });
        items.Add(new Member() { Name = "Tom", Age = 5 });
        _repository.Add(families[1].Name, items);
    }

    private IList<Family> _families;
    public IList<Family> Families
    {
        get { return _families; }
        set { _families = value; OnPropertyChanged("Families"); }
    }

    private Family _selectedFamily;
    public Family SelectedFamily
    {
        get { return _selectedFamily; }
        set { _selectedFamily = value; OnPropertyChanged("SelectedFamily"); }
    }

    private ICommand _familySelectionChangedCommand;
    public ICommand FamilySelectionChangedCommand
    {
        get
        {
            return _familySelectionChangedCommand ?? (_familySelectionChangedCommand = new DelegateCommand(
                () =>
                {
                    Members = (IList<Member>)_repository[SelectedFamily.Name];
                }
            ));
        }
    }
}

XAML: XAML:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*" />
        <ColumnDefinition Width="3*" />
    </Grid.ColumnDefinitions>
    <ListBox ItemsSource="{Binding Families}" SelectedItem="{Binding SelectedFamily}">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectionChanged">
                <i:InvokeCommandAction Command="{Binding FamilySelectionChangedCommand}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </ListBox>
    <Grid Grid.Column="1">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <fsx:ListViewExtended Margin="10" Grid.Row="0"
                            ItemsSource="{Binding Members}" 
                            SelectedItemsList="{Binding SelectedFamily.SelectedMembers, Mode=TwoWay}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Include">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <CheckBox IsChecked="{Binding Path=IsSelected, 
                                RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListViewItem}}}" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
                    <GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
                </GridView>
            </ListView.View>
            <!--<i:Interaction.Behaviors>
            <fsx:ListViewMultiSelectionBehavior 
                SelectedItems="{Binding SelectedFamily.SelectedMembers}" />
        </i:Interaction.Behaviors>-->
        </fsx:ListViewExtended>
        <ListView Margin="10" Grid.Row="1" 
                ItemsSource="{Binding SelectedFamily.SelectedMembers}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
                    <GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Grid>

Apart from above implementation, i have also tried using the approach given in this post but no luck to get 100% results. 除了上面的实现,我也用这个给出的方法试了 ,但没有运气得到100个%的结果。

The problem here is that you can't set a source property of the generic type IList<Member> to a non-generic IList which is the type of the SelectedItems property of the ListView. 这里的问题是您不能将通用类型IList <Member>的source属性设置为非通用IList,这是ListView的SelectedItems属性的类型。 If you try this yourself you will notice that it won't even compile: 如果您自己尝试这样做,您会发现它甚至不会编译:

IList<Member> members = listView1.SelectedItems; //WON'T COMPILE

The generic IList<T> interface does not extend the IList interface so these are two completely different types. 通用IList <T>接口不会扩展IList接口,因此它们是两种完全不同的类型。 It's like trying to set an int property to a string. 就像试图将int属性设置为字符串一样。 It just won't work. 只是行不通。

If you change the type of the SelectedMembers property of your Family class to the non-generic IList it will work: 如果将Family类的SelectedMembers属性的类型更改为非通用IList,它将起作用:

public class Family : BindableBase
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; OnPropertyChanged("Name"); }
    }

//change the type here:
    private IList _selectedMembers;
    public IList SelectedMembers
    {
        get { return _selectedMembers; }
        set { _selectedMembers = value; OnPropertyChanged("SelectedMembers"); }
    }

    public override string ToString()
    {
        return Name;
    }
}

there must be some way to generalize it like other built-in control's DependencyProperty work with collections 就像其他内置控件的DependencyProperty与集合一起工作一样,必须有某种方式将其概括化

As mentioned; 如上所述; for you to be able to set the source property to the value of the SelectedItems property of the ListView, the type of the source property must match the type of the SelectedItems property, ie the source property should be a non-generic IList. 为了能够将source属性设置为ListView的SelectedItems属性的值, source属性的类型必须与SelectedItems属性的类型匹配,即source属性应该是非通用IList。

You may also want to read this: https://blog.magnusmontin.net/2014/01/30/wpf-using-behaviours-to-bind-to-readonly-properties-in-mvvm/ . 您可能还需要阅读以下内容: https : //blog.magnusmontin.net/2014/01/30/wpf-using-behaviours-to-bind-to-readonly-properties-in-mvvm/ It's about how to bind to read-only properties using behaviours. 它与如何使用行为绑定到只读属性有关。 The type of the properties must still match though no matter what solution you choose to adopt. 无论选择采用哪种解决方案,属性的类型都必须仍然匹配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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