Silverlight 5 ListBox IsSelected样式绑定坏了吗?

时间:2013-03-27 09:30:41

标签: silverlight mvvm binding

我看到Silverlight 5购买了样式绑定。试图将它应用于ListBox控件,以进行多项选择。我有以下XAML ListBox(代码在WPF应用程序中工作)。

     <ListBox ItemsSource="{Binding Values}" SelectionMode="Multiple">
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem">
                <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>
            </Style>
        </ListBox.ItemContainerStyle>
        <ListBox.ItemTemplate>
            <DataTemplate DataType="ListBoxItem">
                <TextBlock Text="{Binding DisplayValue}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

当我运行这个时,我得到一个绑定错误,它似乎试图找到“Values”集合类型的IsSelected属性,而不是该集合中的每个单独项目。有没有其他人经历过这个?

更新 添加了要重现的完整代码,您需要滚动列表框以查看输出日志中的错误

public class ValueViewModel : INotifyPropertyChanged
{
    private bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            _isSelected = value;
            OnPropertyChanged("IsSelected");
        }
    }

    private string _displayValue;
    public string DisplayValue
    {
        get { return _displayValue; }
        set
        {
            _displayValue = value;
            OnPropertyChanged("DisplayValue");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

public class MainPageViewModel : INotifyPropertyChanged
{

    public ObservableCollection<ValueViewModel> _values;
    public ObservableCollection<ValueViewModel> Values
    {
        get { return _values; }
        set
        {
            _values = value;
            OnPropertyChanged("Values");
        }
    }

    public MainPageViewModel()
    {
        Values = new ObservableCollection<ValueViewModel>();

        for (var i = 0; i < 50; i++)
            Values.Add(new ValueViewModel() { DisplayValue = i.ToString(), IsSelected = (i % 5) == 0 });
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

和XAML:     

<Grid x:Name="LayoutRoot" Background="White" >
    <Grid.Resources>
        <viewmodels:MainPageViewModel x:Key="vmMainPage"/>
    </Grid.Resources>
    <Grid x:Name="workGrid" DataContext="{Binding Source={StaticResource vmMainPage}}">
        <ListBox ItemsSource="{Binding Values}" SelectionMode="Multiple" Height="100">
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>
                </Style>
            </ListBox.ItemContainerStyle>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Margin="5" Text="{Binding DisplayValue}"/>
                        <TextBlock Margin="5" Text="{Binding IsSelected}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Grid>

更新2 似乎错误的问题在于,在可滚动的情况下,如果选择项目1,然后向下滚动并选择项目49(在上面的示例中),则第一个选择将丢失。

3 个答案:

答案 0 :(得分:1)

我无法重现它。这对我来说可以。这是一个基于您的代码的完整工作示例。我注意到的一个问题是,当呈现ListBoxItem时,它会自动将数据对象上的属性设置为false,而不管它是否为真。因此,如果您加载一个列表并设置其中一些要预先选择的项目,则在呈现ListBoxItem时将取消选择所有项目。防止这种情况的一种方法是使用Dispatcher.BeginInvoke并在那里设置所选项。请参阅下面的代码中的我的评论。

XAML:

<UserControl x:Class="SilverlightApplication12.MainPage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             d:DesignHeight="300"
             d:DesignWidth="400">

    <Grid x:Name="LayoutRoot"
          Background="White">

        <ListBox ItemsSource="{Binding Entities}"
                 SelectionMode="Multiple">
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="IsSelected"
                            Value="{Binding Path=IsSelected, Mode=TwoWay}" />
                </Style>
            </ListBox.ItemContainerStyle>
            <ListBox.ItemTemplate>
                <DataTemplate DataType="ListBoxItem">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}" />
                        <TextBlock Margin="10 0 0 0"
                                   Text="IsSelected:" />
                        <TextBlock Margin="5 0 0 0"
                                   Text="{Binding IsSelected}"></TextBlock>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

    </Grid>
</UserControl>

代码隐藏+实体类:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace SilverlightApplication12
{
    public partial class MainPage : UserControl, INotifyPropertyChanged
    {
        private ObservableCollection<MyEntity> _Entities;

        public ObservableCollection<MyEntity> Entities
        {
            get { return _Entities; }
            set
            {
                _Entities = value;
                OnPropertyChanged("Entities");
            }
        }

        public MainPage()
        {
            InitializeComponent();

            Entities = new ObservableCollection<MyEntity>();
            Entities.Add(new MyEntity()
            {
                Name = "One",
                IsSelected = false,
            });
            Entities.Add(new MyEntity()
            {
                Name = "Two",
                IsSelected = true,
                //Even though this is initially true it does not matter. 
                //When the ListBoxItem is rendered it sets the property to false. 
            });
            Entities.Add(new MyEntity()
            {
                Name = "Three",
                IsSelected = false,
            });

            LayoutRoot.DataContext = this;

            //Enable the following line to set the 2nd item to selected when the page is loaded. 
            //Dispatcher.BeginInvoke(() => Entities[1].IsSelected = true);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    public class MyEntity : INotifyPropertyChanged
    {
        private string _Name;

        public string Name
        {
            get { return _Name; }
            set
            {
                _Name = value;
                OnPropertyChanged("Name");
            }
        }

        private bool _IsSelected;

        public bool IsSelected
        {
            get
            {
                return _IsSelected;
            }
            set
            {
                _IsSelected = value;
                OnPropertyChanged("IsSelected");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

答案 1 :(得分:0)

除了<DataTemplate DataType="ListBoxItem">之外的所有内容对我来说都很好。


如果Values集合是ListBoxItems的集合,则不需要IsSelected绑定。

否则,DataTemplate上的DataType是错误的,应该留空。

答案 2 :(得分:0)

所以我设法找到了一个似乎可以满足我需求的工作场所。一旦Loaded事件被触发,它将设置已加载的值。它包装了MouseDown事件以设置选择状态。它不是一个真正的数据绑定,但可以完成工作,并且仍然保持View清除代码。

    <ListBox ItemsSource="{Binding Values}" SelectionMode="Multiple" Height="100">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Margin="2" Text="{Binding DisplayValue, Mode=TwoWay}">
                    <i:Interaction.Triggers>
                        <i:EventTrigger EventName="Loaded">
                            <ei:ChangePropertyAction 
                                        TargetObject="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
                                        PropertyName="IsSelected" 
                                        Value="{Binding IsSelected}"/>
                        </i:EventTrigger>
                        <i:EventTrigger EventName="MouseLeftButtonDown">
                            <ei:ChangePropertyAction 
                                        TargetObject="{Binding}"
                                        PropertyName="IsSelected" 
                                        Value="{Binding IsSelected, Converter={StaticResource invertBooleanConverter}}"/>
                        </i:EventTrigger>
                    </i:Interaction.Triggers>  
                </TextBlock>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>