我想通过将ObservableCollection
绑定到string
的{{1}}属性并将项目模板设置为ItemsSource
来操纵ListBox
TextBox
个ObservableCollection
}。
我的问题是TextBox
中的项目在ListBox
包含的 <Window x:Class="ListBoxUpdate.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxUpdate" Height="300" Width="300"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Show items" Click="HandleButtonClick"/>
<TextBlock Grid.Row="1" x:Name="textBlock" />
</Grid>
<ListBox
Grid.Column="1"
ItemsSource="{Binding Strings, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding ., Mode=TwoWay}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
项目中进行编辑时不会得到更新。我做错了什么?
最小工作示例的XAML是
using System;
using System.Collections.ObjectModel;
using System.Windows;
namespace ListBoxUpdate
{
public partial class Window1 : Window
{
public ObservableCollection<string> Strings { get; set; }
public Window1()
{
InitializeComponent();
Strings = new ObservableCollection<string>(new string[] { "one", "two", "three" });
this.DataContext = this;
}
void HandleButtonClick(object sender, RoutedEventArgs e)
{
string text = "";
for (int i = 0; i < Strings.Count; i++) {
text += Strings[i] + Environment.NewLine;
}
textBlock.Text = text;
}
}
}
虽然相应的代码隐藏是
dplyr::slice(df, match(c(3, 5, 7), x))
非常感谢您的建议。
答案 0 :(得分:1)
正如@BrandonKramer和@Peter Duniho所展示的那样,解决方案是数据绑定不能改变它所绑定的对象本身,只能改变该对象的属性。
因此,我必须创建一个包装类,其属性将是我想要操作的字符串。代码隐藏现在是
<ListBox
Grid.Column="1"
ItemsSource="{Binding Strings, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Content, Mode=TwoWay}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
并且必须仅在一个点修改XAML
LostFocus
谢谢@BrandonKramer和@Peter Duniho。
更新:我觉得定义一个包装器只是为了操纵对象感到不舒服,而且在列表中编辑对象本身而不是其中一个属性的问题在我看来很普遍,所以我试图找到另一个解决方案。我宁愿决定挂钩TextBox
中ItemTemplate
的{{1}}事件。
在这种情况下,问题是从模板化ListBox
中找到TextBox
中的索引,该索引正在失去焦点。我无法使用SelectedIndex
的{{1}}属性,因为当ListBox
触发时,它已设置为不同的LostFocus
。
我搜索了很多,并在这里找到了Dennis Troller的答案:WPF ListBoxItems with DataTemplates - How do I reference the CLR Object bound to ListBoxItem from within the DataTemplate?诀窍是让ListBoxItem
失去焦点并DataContext
使用TextBox
ItemContainerGenerator
首先标识容器(使用ListBox
)然后获取列表中的索引(使用ItemContainerGenerator.ContainerFromItem
)。
代码隐藏现在是
ItemContainerGenerator.IndexFromContainer
完整的XAML如下(我单向绑定了文本):
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
namespace ListBoxUpdate
{
public partial class Window1 : Window
{
public ObservableCollection<string> Strings { get; set; }
public Window1()
{
InitializeComponent();
this.Strings = new ObservableCollection<string>(new string[] { "one", "two", "three" });
this.DataContext = this;
}
void HandleButtonClick(object sender, RoutedEventArgs e)
{
string text = "";
for (int i = 0; i < Strings.Count; i++) {
text += Strings[i] + Environment.NewLine;
}
textBlock.Text = text;
}
void HandleTextBoxLostFocus(object sender, RoutedEventArgs e)
{
// https://stackoverflow.com/questions/765984/wpf-listboxitems-with-datatemplates-how-do-i-reference-the-clr-object-bound-to?rq=1, @Dennis Troller's answer.
int index;
object item;
DependencyObject container;
TextBox textBox = sender as TextBox;
if (textBox == null) return;
item = textBox.DataContext;
container = listBox.ItemContainerGenerator.ContainerFromItem(item);
if (container != null) {
index = listBox.ItemContainerGenerator.IndexFromContainer(container);
if (textBox.Text != Strings[index]) {
Strings[index] = textBox.Text;
}
}
}
}
}