ListView SelectedItems绑定:为什么列表始终为null

时间:2016-01-16 00:57:06

标签: c# xaml binding mvvm-light uwp

我正在使用Mvvm Light和Behaviors SDK开发UWP应用程序。我定义了一个多选ListView:

<ListView
    x:Name="MembersToInviteList"
    IsMultiSelectCheckBoxEnabled="True"
    SelectionMode="Multiple"
    ItemsSource="{Binding Contacts}"
    ItemTemplate="{StaticResource MemberTemplate}">

</ListView>

我想通过一个绑定到MVVM-Light RelayCommand的按钮来获取包含所选项目的列表:

<Button
    Command="{Binding AddMembersToEvent}"
    CommandParameter="{Binding ElementName=MembersToInviteList, Path=SelectedItems}"
    Content="Ok"/>

RelayCommand(MVVM-Light框架):

private RelayCommand<object> _addMembersToEvent;
public RelayCommand<object> AddMembersToEvent
{
    get
    {
        return _addMembersToEvent
            ?? (_addMembersToEvent = new RelayCommand<object>(
               (selectedMembers) =>
               {
                   // Test
                   // selectedMembers is always null!
               }));
    }
}

我在命令中放了一个断点,我注意到selectedMembers总是null,尽管我选择了各种项目。通过控制台输出我没有看到任何绑定错误或其他。

另外,如果我作为CommandParameter传递整个列表,并在命令的定义中放置断点,我注意到我无法访问SelectedItems和SelecteRanges值。

<DataTemplate x:Name="MemberTemplate">

    <Viewbox MaxWidth="250">
        <Grid Width="250"
              Margin="5, 5, 5, 5"
              Background="{StaticResource MyLightGray}"
              BorderBrush="{StaticResource ShadowColor}"
              BorderThickness="0, 0, 0, 1"
              CornerRadius="4"
              Padding="5">

            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="1*" />
            </Grid.ColumnDefinitions>

            <Grid Grid.Column="0"
                  Width="45"
                  Height="45"
                  Margin="5,0,5,0"
                  VerticalAlignment="Center"
                  CornerRadius="50">

                <Grid.Background>
                    <ImageBrush AlignmentX="Center"
                                AlignmentY="Center"
                                ImageSource="{Binding Image.Url,
                                                      Converter={StaticResource NullGroupImagePlaceholderConverter}}"
                                Stretch="UniformToFill" />
                </Grid.Background>

            </Grid>

            <TextBlock Grid.Column="1"
                       Margin="3"
                       VerticalAlignment="Center"
                       Foreground="{StaticResource ForegroundTextOverBodyColor}"
                       Style="{StaticResource LightText}"
                       Text="{Binding Alias}" />

        </Grid>
    </Viewbox>

</DataTemplate>

是什么原因?我怎样才能获得这样的清单?

5 个答案:

答案 0 :(得分:6)

在igralli的博客中描述了从ViewModel中的ListView传递SelectedItems(使用RelayCommands)的解决方案之一。

Pass ListView SelectedItems to ViewModel in Universal apps

尝试以下代码从参数中获取所选对象。

    private RelayCommand<IList<object>> _addMembersToEvent;
    public RelayCommand<IList<object>> AddMembersToEvent
    {
        get
        {
            return _addMembersToEvent
                   ?? (_addMembersToEvent = new RelayCommand<IList<object>>(
                       selectedMembers =>
                       {
                           List<object> membersList = selectedMembers.ToList();
                       }));
        }
    }

答案 1 :(得分:1)

我为没有MVVM-Light的情况做了一个小例子,它完美无缺。

问题可能在RelayCommand类中。我写了以下内容:

public class RelayCommand<T> : ICommand
{
    private readonly Action<T> execute;
    private readonly Predicate<T> canExecute;

    public RelayCommand(Action<T> execute, Predicate<T> canExecute = null )
    {
        if (execute == null)
            throw new ArgumentNullException("execute");
        this.execute = execute;
        this.canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        if (canExecute == null)
            return true;
        return canExecute((T) parameter);
    }

    public void Execute(object parameter)
    {
        execute((T) parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
}

答案 2 :(得分:1)

感谢Roman的回答,我想出了如何解决这个问题:

首先,罗马建议:

private RelayCommand<IList<object>> _addMembersToEvent;
public RelayCommand<IList<object>> AddMembersToEvent
{
    get
    {
        return _addMembersToEvent
               ?? (_addMembersToEvent = new RelayCommand<IList<object>>(
                   selectedMembers =>
                   {
                       List<object> membersList = selectedMembers.ToList();
                   }));
    }
}

然后,XAML:

<Button
    Command="{Binding AddMembersToEvent}"
    CommandParameter="{Binding ElementName=MembersToInviteList, Converter={StaticResource ListViewSelectedItemsConverter}}"
    Content="Ok"/>

这里的不同之处在于我将整个列表作为参数传递,而不是它的SelectedItems属性。然后,使用IValueConverter我从ListView对象转换为IList<object>的{​​{1}}:

SelectedMember

通过这种方式,public class ListViewSelectedItemsConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { var listView = value as ListView; return listView.SelectedItems; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } 获得了正确的列表,而不是空值。

答案 3 :(得分:0)

我找不到理由,但您可以通过在Contact对象上添加“IsSelected”属性并在模板上放置一个复选框来轻松解决问题:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid.RowDefinitions>
        <RowDefinition Height="*"></RowDefinition>
        <RowDefinition Height="Auto"></RowDefinition>
        <RowDefinition Height="Auto"></RowDefinition>
    </Grid.RowDefinitions>

    <ListView ItemsSource="{Binding Contacts}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <CheckBox Content="{Binding Name}" IsChecked="{Binding IsSelected, Mode=TwoWay}"></CheckBox>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

    <TextBlock Grid.Row="1" Text="{Binding SelectedItemsOutput}"></TextBlock>
    <Button Grid.Row="2" Content="What is checked?" Command="{Binding GoCommand}"></Button>
</Grid>

和VM等:

public class MainViewModel : INotifyPropertyChanged
{
    private string _selectedItemsOutput;

    public ObservableCollection<Contact> Contacts { get; set; } = new ObservableCollection<Contact> { new Contact() { Id = 1, Name = "Foo" }, new Contact() { Id = 2, Name = "Bar" } };

    public ICommand GoCommand => new RelayCommand(Go);

    public string SelectedItemsOutput
    {
        get { return _selectedItemsOutput; }
        set
        {
            if (value == _selectedItemsOutput) return;
            _selectedItemsOutput = value;
            OnPropertyChanged();
        }
    }

    void Go()
    {
        SelectedItemsOutput = string.Join(", ", Contacts.Where(x => x.IsSelected).Select(x => x.Name));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

public class Contact : INotifyPropertyChanged
{
    private bool _isSelected;

    public int Id { get; set; }
    public string Name { get; set; }

    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            if (value == _isSelected) return;
            _isSelected = value;
            OnPropertyChanged();
        }
    }

    public override string ToString()
    {
        return Name;
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        DataContext = new MainViewModel();
    }
}

答案 4 :(得分:0)

只需我的五美分,可能绝对是一个很长的镜头,但你应该检查this

  

如果ItemsSource实现IItemsRangeInfo,则不会根据列表中的选择更新SelectedItems集合。请改用SelectedRanges属性。

我只是根据Tomtom的答案猜测,因为他说他几乎和你一样,并且得到了一个有效的解决方案。也许差异在于这个小细节。我自己还没有检查过。