1

Silverlight5で購入したスタイルのバインディングが表示されます。複数選択のために、リストボックスコントロールに適用しようとしました。次のXAMLリストボックスがあります(コードは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(上記の例)を選択すると、最初の選択が失われることです。

4

3 に答える 3

1

再現できません。それは私にとってはうまくいきます。これは、コードに基づいた完全な実例です。私が気付いた問題の1つは、ListBoxItemがレンダリングされると、最初からtrueであるかどうかに関係なく、データオブジェクトのプロパティが自動的にfalseに設定されることです。したがって、リストをロードしてそのアイテムの一部を事前に選択するように設定すると、ListBoxItemsがレンダリングされるときにすべてのアイテムが選択解除されます。これを防ぐ1つの方法は、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

これ以外はすべて<DataTemplate DataType="ListBoxItem">私には問題ないように見えます。


ValuesコレクションがListBoxItemsのコレクションである場合、IsSelectedバインディングは必要ありません。

それ以外の場合は、DataTemplateのDataTypeが間違っているため、空白のままにしておく必要があります。

于 2013-03-27T10:18:14.117 に答える
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 に答える