双向链接到ListView的所有SelectedItems的属性?

时间:2019-05-21 19:16:37

标签: c# wpf xaml listview data-binding

我有一个ListView绑定到对象的ObservableCollection。紧接着,我有一堆TextBox和ComboBoxes被TwoWay绑定到该ListView的SelectedItem的属性。我的ListView中的项目具有INotifyPropertyChanged。这样,用户可以从ListView中选择一个项目并编辑其属性。

但是,编辑大量项目需要很长时间,因此我希望用户能够选择多个项目,并使用ListView旁边的控件一次编辑所有选定项目的属性。

我已经尝试将DataContext更改为ListView的SelectedItems属性,但这没用。

有人可以告诉我该怎么做吗?

编辑: 为了澄清,当用户选择多个项目时,我希望ListView旁边的编辑控件不显示任何内容,然后仅当用户更改这些控件中的某些内容时,更改才进入所有SelectedItems,并且更改在TextBox中保持可见或ComboBox,因为那时所有SelectedItems中的属性都相同。

<StackPanel x:Name="EditPanel" Grid.Row="0" Grid.RowSpan="2" DataContext="{Binding SelectedItem, ElementName=LayersList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CanVerticallyScroll="True">
    <TextBlock FontSize="14" HorizontalAlignment="Stretch" Foreground="#FFD6D6D6" Margin="0,10,0,0"><Run Text="Name:"/></TextBlock>
    <TextBox x:Name="SelectedNameBox" HorizontalAlignment="Stretch" TextWrapping="NoWrap" Foreground="#FFD6D6D6" Text="{Binding Name, Mode=TwoWay}"/>
    <TextBlock FontSize="14" HorizontalAlignment="Stretch" Foreground="#FFD6D6D6" Margin="0,10,0,0"><Run Text="Hitsound info:"/></TextBlock>
    <ComboBox x:Name="SelectedSampleSetBox" Margin="0,10,0,0" HorizontalAlignment="Stretch" Text="{Binding SampleSetString, Mode=TwoWay}" Cursor="Hand">
        <ComboBoxItem Content="Normal" HorizontalAlignment="Left" Cursor="Hand"/>
        <ComboBoxItem Content="Soft" HorizontalAlignment="Left" Cursor="Hand"/>
        <ComboBoxItem Content="Drum" HorizontalAlignment="Left" Cursor="Hand"/>
    </ComboBox>
    <ComboBox x:Name="SelectedHitsoundBox" Margin="0,10,0,0" HorizontalAlignment="Stretch" Text="{Binding HitsoundString, Mode=TwoWay}" Cursor="Hand">
        <ComboBoxItem Content="Normal" HorizontalAlignment="Left" Cursor="Hand"/>
        <ComboBoxItem Content="Whistle" HorizontalAlignment="Left" Cursor="Hand"/>
        <ComboBoxItem Content="Finish" HorizontalAlignment="Left" Cursor="Hand"/>
        <ComboBoxItem Content="Clap" HorizontalAlignment="Left" Cursor="Hand"/>
    </ComboBox>
    ...
</StackPanel>

2 个答案:

答案 0 :(得分:1)

在描述问题时,您似乎并没有考虑到这一点。

如果您有一个列表和一堆显示所选项目属性的控件,那么当选择了多个项目时,您希望在哪里显示属性数据?例如,如果您有一个Person类,例如:

public class Person
{
    public string Name { get; set; }
    public string City { get; set; }
    public int Age { get; set; }
}

当用户从列表中选择两个不同的Person实例时,您是否希望分配给Name属性的文本框显示所有选定的名称?否则,如果您希望它们在不同的文本框中,则必须动态创建与用户选择的项目一样多的文本框。我觉得这两种都不是理想的解决方案。

这是一个替代解决方案;您将数据显示在DataGrid中。 我在启用DataGrid的情况下使用了最简单的AutoGeneratingColumns

<DataGrid Grid.Row="0" Margin="4"
          Name="DataGridPersons"
          AutoGenerateColumns="True"
          ItemsSource="{Binding Persons}"/>

然后,在后面的代码中(理想情况下,您将使用MVVM模式,在这种情况下,您将在ViewModel中使用),只需填充数据列表即可。

public ObservableCollection<Person> Persons { get; set; }

public MainWindow()
{
    InitializeComponent();

    Persons = new ObservableCollection<Person>
    {
        new Person() { Name = "Jane", City = "NY", Age = 23 },
        new Person() { Name = "Chelsea", City = "LA", Age = 27 },
        new Person() { Name = "Chris", City = "Chicago", Age = 25 }
    };

    DataContext = this;
}

默认情况下,DataGrid是可编辑的,并且会记录您所做的更改。

可编辑的DataGrid

enter image description here


编辑:

在OP编辑了问题之后,这是一个新答案。

在这种情况下,我会摆脱SelectedItem,而拥有与您的类对象中的每个属性相对应的string属性。

假设您的Person类是这样的:

public class Person
{
    public string Name { get; set; }
    public string City { get; set; }
}

然后,我将拥有ObservableCollection个属性,以及与您在班级中拥有的属性一样多的string个属性;在这种情况下是两个。

public ObservableCollection<Person> Persons { get; set; }

private string _editName = null;
public string EditName
{
    get { return _editName; }
    set
    {
        _editName = value;
        OnPropertyChanged("EditName");
    }
}

private string _editCity = null;
public string EditCity
{
    get { return _editCity; }
    set
    {
        _editCity = value;
        OnPropertyChanged("EditCity");
    }
}

然后将文本框绑定到这些属性:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <ListBox Grid.Column="0" Grid.Row="0" Grid.RowSpan="3"
             Margin="8" Name="ItemListBox"
             ItemsSource="{Binding Persons}"
             DisplayMemberPath="Name"/>

    <TextBox Grid.Column="1" Grid.Row="0"
             Margin="8" Name="TxtName"
             TextChanged="TxtName_TextChanged"
             Text="{Binding EditName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    <TextBox Grid.Column="1" Grid.Row="1"
             Margin="8" Name="TxtCity"
             TextChanged="TxtCity_TextChanged"
             Text="{Binding EditCity, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>

然后在每个文本框的TextChanged事件中,我将更新您的ObservableCollection

private void TxtName_TextChanged(object sender, TextChangedEventArgs e)
{
    foreach (var person in Persons)
    {
        person.Name = EditName;
    }
}

private void TxtCity_TextChanged(object sender, TextChangedEventArgs e)
{
    foreach (var person in Persons)
    {
        person.City = EditCity;
    }
}

答案 1 :(得分:0)

我最终没有对编辑控件使用数据绑定。相反,我在ListView的SelectionChangedEvent上显式更新了它们。其中还描述了如何一次显示多个选定项目的逻辑。

suppressEvents = true;

if (selectedLayers.TrueForAll(o => o.Name == selectedLayer.Name)) {
    SelectedNameBox.Text = selectedLayer.Name;
} else {
    SelectedNameBox.Text = "";
}
...

suppressEvents = false;

然后,编辑控件的ChangedEvents将更新所有SelectedItems。

private void SelectedNameBox_TextChanged(object sender, TextChangedEventArgs e) {
    if (suppressEvents) return;

    string t = (sender as TextBox).Text;
    foreach (HitsoundLayer hitsoundLayer in LayersList.SelectedItems) {
        hitsoundLayer.Name = t;
    }
}