1

我看到 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>

当我运行它时,我得到一个绑定错误,它似乎试图在“值”集合的类型上查找 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(在上面的示例中),则第一个选择会丢失。

4

3 回答 3

1

我无法重现它。这对我来说可以。这是基于您的代码的完整工作示例。我确实注意到的一个问题是,当呈现 ListBoxItem 时,它会自动将数据对象上的属性设置为 false,而不管它是否一开始就为 true。因此,如果您加载一个列表并将其中的一些项目设置为预选,则在呈现 ListBoxItems 时,所有项目都将被取消选择。防止这种情况的一种方法是使用 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));
            }
        }
    }
}
于 2013-03-27T10:30:36.687 回答
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>
于 2013-03-27T17:26:17.773 回答
0

除了这个,我觉得一切都<DataTemplate DataType="ListBoxItem">很好。


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

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

于 2013-03-27T10:18:14.117 回答