如何将不同列表框的 selectedItem 绑定到一个文本框?
How to bind selectedItem from different listboxes to one textbox?
我是 WPF 的新手,遇到了绑定问题。
我有 3 个列表框要绑定到一个文本框。在 listbox1 中,我有名字 "John Doe",在 listbox2 中有名字 "Brian Warner",在 listbox3 中有名字 "Anne Brown"。
当我点击 "John Doe" 时,它会在文本框中显示 "John Doe",我可以更改他的名字,因为我的 xaml
中有这个
<TextBox x:Name="Name" Text="{Binding ElementName=listbox1, Path=SelectedItem.Name}"/>
现在,当我 select Listbox2 中的项目时,我想将同一文本框的值更改为 "Brian Warner",当我 select 时,将同一文本框的值更改为 "Anne Brown" =] 她在 Listbox3 中的名字。
我还希望能够编辑他们的名字并在列表框中更新它。
我想我需要找到一种方法来将绑定 ElementName 从 Listbox1 更改为 Listbox2,每次我 select 和列表框中的项目更改为 listbox3。
我该怎么做?我很新手,我找不到任何关于如何做到这一点的例子。我希望我能提供更多代码,但我不知道从哪里开始。感谢您的帮助。
更新
我设法通过使用
从专用文本框中的 3 个列表框中获取所需的值
SelectionChanged="listBox_SelectionChanged"
在每个文本框的 xaml 中。
private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
tb_firstName.Text = ((sender as ListBox).SelectedItem as Person).FirstName;
tb_lastName.Text = ((sender as ListBox).SelectedItem as Person).LastName;
tb_occupation.Text = ((sender as ListBox).SelectedItem as Person).Occupation;
tb_characteristics.Text = ((sender as ListBox).SelectedItem as Person).Characteristics;
tb_email.Text = ((sender as ListBox).SelectedItem as Person).Email;
tb_phoneNr.Text = ((sender as ListBox).SelectedItem as Person).PhoneNr;
tb_moreInfo.Text = ((sender as ListBox).SelectedItem as Person).MoreInfo;
tb_group.Text = ((sender as ListBox).SelectedItem as Person).Group;
}
但是当我尝试更改文本框中的值时,它不会在列表框中更新。我该怎么做?
好吧,您可以在代码隐藏等中处理这种绑定。但是,我会实施更简单的解决方案:只需创建 3 个具有相同布局和样式(或不同)的文本框,每个文本框都绑定到一个特定的列表框,当您引发列表框的 onItemSelected 事件时,只需显示相关的文本框并折叠另一个。哇啦!没有任何可能导致麻烦的绑定处理。
一个简单的解决方案可能是
1-定义一个保存列表框引用的变量
2- 对所有三个使用 OnItemSelectionChange 事件
3-更新标志以指示调用哪个控制事件
4-更改每个事件的文本框的值
5- 当文本框中的值更改时,使用标志更改列表框中的值。
一个文本框和三个列表框绑定是不合逻辑的。
如果我对问题的理解正确,你有一个编辑字段(TextBox
),你想用它来显示和编辑数据的 Name
属性对象 select 在 ListBox
中编辑。有多个这样的 ListBox
对象,用户应该能够编辑 select 在最近使用的 ListBox
中编辑的数据对象。
事实证明,这并不是一件完全微不足道的事情。我同意 Ben Cohen 的回复,其中一种方法是在焦点改变时显示和隐藏 不同的 TextBox
个实例。一种相关但不同的方法是只有一个 TextBox
并根据焦点变化更新其 Binding
属性。
但它比这更复杂,因为当用户 select 是 ListBox
中的项目时,ListBox
本身不是接收焦点的对象。它是 ListBox
中的 ListBoxItem
。在 XAML 中似乎没有方便的方法来访问 select 编辑的 ListBoxItem
。但即使你可以,还有另一个问题......
此外,您不能简单地将每个 TextBox
的可见性绑定到聚焦状态(或合适的代理),因为一旦用户实际点击 TextBox
, ListBox
/它的代理失去焦点。
无论如何,稍微解决了这个问题(坦率地说,可能比我应该做的更多,但是嘿......我在这里也学到了一些新东西:)),我想出了一个我认为确实如此的代码示例正是您在这里要求的(我希望这正是您想要的 :)):
XAML:
<Window x:Class="TestSO30283586BindOnFocus.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:TestSO30283586BindOnFocus"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="dataTemplate1" DataType="{x:Type local:A}">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
<local:BoolToVisibilityConverter x:Key="boolToVisibilityConverter1"/>
<Style TargetType="ListBox">
<Setter Property="ItemTemplate" Value="{StaticResource dataTemplate1}"/>
<Setter Property="Tag">
<Setter.Value>
<system:Boolean>False</system:Boolean>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ListBoxItem">
<EventSetter Event="GotFocus" Handler="listBoxItem_Focus"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="FontSize" Value="16"/>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListBox x:Name="listBox1"
Grid.Row="0">
<ListBox.Items>
<local:A Name="John"/>
</ListBox.Items>
</ListBox>
<ListBox x:Name="listBox2"
Grid.Row="1">
<ListBox.Items>
<local:A Name="Jacob"/>
</ListBox.Items>
</ListBox>
<ListBox x:Name="listBox3"
Grid.Row="2">
<ListBox.Items>
<local:A Name="Jingleheimer"/>
</ListBox.Items>
</ListBox>
<TextBox Text="{Binding ElementName=listBox1, Path=SelectedItem.Name, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding ElementName=listBox1, Path=Tag, Converter={StaticResource boolToVisibilityConverter1}}"/>
<TextBox Text="{Binding ElementName=listBox2, Path=SelectedItem.Name, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding ElementName=listBox2, Path=Tag, Converter={StaticResource boolToVisibilityConverter1}}"/>
<TextBox Text="{Binding ElementName=listBox3, Path=SelectedItem.Name, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding ElementName=listBox3, Path=Tag, Converter={StaticResource boolToVisibilityConverter1}}"/>
</Grid>
</Window>
C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void listBoxItem_Focus(object sender, RoutedEventArgs e)
{
ListBox[] listBoxes = { listBox1, listBox2, listBox3 };
ListBoxItem item = (ListBoxItem)sender;
ListBox listBoxParent = GetParent<ListBox>(item);
foreach (ListBox listBox in listBoxes)
{
listBox.Tag = object.ReferenceEquals(listBoxParent, listBox);
}
}
private static T GetParent<T>(DependencyObject o) where T : DependencyObject
{
while (o != null && !(o is T))
{
o = VisualTreeHelper.GetParent(o);
}
return (T)o;
}
}
class A : DependencyObject
{
public static readonly DependencyProperty NameProperty = DependencyProperty.Register(
"Name", typeof(string), typeof(A));
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
}
class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!(value is bool))
{
return Binding.DoNothing;
}
return (bool)value ? Visibility.Visible : Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
有一些代码隐藏来处理我无法在标记中解决的问题。特别是,代码隐藏包含一个事件处理程序,只要 ListBoxItem
获得焦点就会触发该事件处理程序。发生这种情况时,事件处理程序会更新每个 ListBox
的 Tag
属性,以便将包含 ListBoxItem
的那个设置为 true
,而另外两个设置为 false
.
这样,我用Tag
属性作为ListBoxItem
IsFocused
属性的代理,让我的TextBox
元素将它们的 Visibility
属性绑定到它。请注意,我仅在 收到 焦点时设置 Tag
。这样,可见性保持设置,即使当用户 selects TextBox
元素失去焦点时也是如此。
当您 运行 这个程序时,每个 ListBox
只用一个项目初始化。当您 select 一个项目时,它会显示在右侧的 TextBox
中。随着三个 ListBox
元素及其项之间的焦点变化,显示和隐藏不同的 TextBox
元素,但对用户而言,它看起来就像正在更新单个 TextBox
以反映当前 select离子.
如果您编辑 TextBox
中的文本,链接项目的 Name
属性 会像您一样更新。这种变化当然反映在 ListBox
.
中的显示中
我不能向您保证这是最好的 方法。但它确实有效。我对此最大的抱怨是 Tag
属性 的劫持。但事实是,只要您不将 属性 用于其他用途,将它用于此目的可能没问题。
我实际上希望一些真正的 WPF 专家会出现并告诉我我是个傻瓜,并且有更好的方法来实现这一点。但与此同时,我希望以上内容能够充分满足您的需要。 :)
我在上面提到,另一种方法是更新单个 TextBox
元素的 Binding
。我没有显示代码,但不会有太大的不同。你当然只有一个 TextBox
,GotFocus
事件处理程序不会与 ListBox.Tag
属性 混淆,而是检索 GotFocus
的实际数据对象=22=] 发送事件,并相应地为单个 TextBox
对象配置一个新的 Binding
对象。
该方法将避免使用 Tag
属性,甚至可能稍微更有效(或者可能不是……它可以采用任何一种方式)。我个人发现操纵绑定的代码比上面仅设置 属性 值的代码复杂一些。但是由于需要沿着可视化树向上移动以找到父级 ListBox
,因此该代码变得复杂。所以也许是洗了。
除了这些差异之外,代码的结构仍然大同小异。
我是 WPF 的新手,遇到了绑定问题。
我有 3 个列表框要绑定到一个文本框。在 listbox1 中,我有名字 "John Doe",在 listbox2 中有名字 "Brian Warner",在 listbox3 中有名字 "Anne Brown"。
当我点击 "John Doe" 时,它会在文本框中显示 "John Doe",我可以更改他的名字,因为我的 xaml
中有这个<TextBox x:Name="Name" Text="{Binding ElementName=listbox1, Path=SelectedItem.Name}"/>
现在,当我 select Listbox2 中的项目时,我想将同一文本框的值更改为 "Brian Warner",当我 select 时,将同一文本框的值更改为 "Anne Brown" =] 她在 Listbox3 中的名字。 我还希望能够编辑他们的名字并在列表框中更新它。
我想我需要找到一种方法来将绑定 ElementName 从 Listbox1 更改为 Listbox2,每次我 select 和列表框中的项目更改为 listbox3。
我该怎么做?我很新手,我找不到任何关于如何做到这一点的例子。我希望我能提供更多代码,但我不知道从哪里开始。感谢您的帮助。
更新
我设法通过使用
从专用文本框中的 3 个列表框中获取所需的值SelectionChanged="listBox_SelectionChanged"
在每个文本框的 xaml 中。
private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
tb_firstName.Text = ((sender as ListBox).SelectedItem as Person).FirstName;
tb_lastName.Text = ((sender as ListBox).SelectedItem as Person).LastName;
tb_occupation.Text = ((sender as ListBox).SelectedItem as Person).Occupation;
tb_characteristics.Text = ((sender as ListBox).SelectedItem as Person).Characteristics;
tb_email.Text = ((sender as ListBox).SelectedItem as Person).Email;
tb_phoneNr.Text = ((sender as ListBox).SelectedItem as Person).PhoneNr;
tb_moreInfo.Text = ((sender as ListBox).SelectedItem as Person).MoreInfo;
tb_group.Text = ((sender as ListBox).SelectedItem as Person).Group;
}
但是当我尝试更改文本框中的值时,它不会在列表框中更新。我该怎么做?
好吧,您可以在代码隐藏等中处理这种绑定。但是,我会实施更简单的解决方案:只需创建 3 个具有相同布局和样式(或不同)的文本框,每个文本框都绑定到一个特定的列表框,当您引发列表框的 onItemSelected 事件时,只需显示相关的文本框并折叠另一个。哇啦!没有任何可能导致麻烦的绑定处理。
一个简单的解决方案可能是
1-定义一个保存列表框引用的变量 2- 对所有三个使用 OnItemSelectionChange 事件 3-更新标志以指示调用哪个控制事件 4-更改每个事件的文本框的值 5- 当文本框中的值更改时,使用标志更改列表框中的值。
一个文本框和三个列表框绑定是不合逻辑的。
如果我对问题的理解正确,你有一个编辑字段(TextBox
),你想用它来显示和编辑数据的 Name
属性对象 select 在 ListBox
中编辑。有多个这样的 ListBox
对象,用户应该能够编辑 select 在最近使用的 ListBox
中编辑的数据对象。
事实证明,这并不是一件完全微不足道的事情。我同意 Ben Cohen 的回复,其中一种方法是在焦点改变时显示和隐藏 不同的 TextBox
个实例。一种相关但不同的方法是只有一个 TextBox
并根据焦点变化更新其 Binding
属性。
但它比这更复杂,因为当用户 select 是 ListBox
中的项目时,ListBox
本身不是接收焦点的对象。它是 ListBox
中的 ListBoxItem
。在 XAML 中似乎没有方便的方法来访问 select 编辑的 ListBoxItem
。但即使你可以,还有另一个问题......
此外,您不能简单地将每个 TextBox
的可见性绑定到聚焦状态(或合适的代理),因为一旦用户实际点击 TextBox
, ListBox
/它的代理失去焦点。
无论如何,稍微解决了这个问题(坦率地说,可能比我应该做的更多,但是嘿......我在这里也学到了一些新东西:)),我想出了一个我认为确实如此的代码示例正是您在这里要求的(我希望这正是您想要的 :)):
XAML:
<Window x:Class="TestSO30283586BindOnFocus.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:TestSO30283586BindOnFocus"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="dataTemplate1" DataType="{x:Type local:A}">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
<local:BoolToVisibilityConverter x:Key="boolToVisibilityConverter1"/>
<Style TargetType="ListBox">
<Setter Property="ItemTemplate" Value="{StaticResource dataTemplate1}"/>
<Setter Property="Tag">
<Setter.Value>
<system:Boolean>False</system:Boolean>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ListBoxItem">
<EventSetter Event="GotFocus" Handler="listBoxItem_Focus"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="FontSize" Value="16"/>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListBox x:Name="listBox1"
Grid.Row="0">
<ListBox.Items>
<local:A Name="John"/>
</ListBox.Items>
</ListBox>
<ListBox x:Name="listBox2"
Grid.Row="1">
<ListBox.Items>
<local:A Name="Jacob"/>
</ListBox.Items>
</ListBox>
<ListBox x:Name="listBox3"
Grid.Row="2">
<ListBox.Items>
<local:A Name="Jingleheimer"/>
</ListBox.Items>
</ListBox>
<TextBox Text="{Binding ElementName=listBox1, Path=SelectedItem.Name, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding ElementName=listBox1, Path=Tag, Converter={StaticResource boolToVisibilityConverter1}}"/>
<TextBox Text="{Binding ElementName=listBox2, Path=SelectedItem.Name, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding ElementName=listBox2, Path=Tag, Converter={StaticResource boolToVisibilityConverter1}}"/>
<TextBox Text="{Binding ElementName=listBox3, Path=SelectedItem.Name, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding ElementName=listBox3, Path=Tag, Converter={StaticResource boolToVisibilityConverter1}}"/>
</Grid>
</Window>
C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void listBoxItem_Focus(object sender, RoutedEventArgs e)
{
ListBox[] listBoxes = { listBox1, listBox2, listBox3 };
ListBoxItem item = (ListBoxItem)sender;
ListBox listBoxParent = GetParent<ListBox>(item);
foreach (ListBox listBox in listBoxes)
{
listBox.Tag = object.ReferenceEquals(listBoxParent, listBox);
}
}
private static T GetParent<T>(DependencyObject o) where T : DependencyObject
{
while (o != null && !(o is T))
{
o = VisualTreeHelper.GetParent(o);
}
return (T)o;
}
}
class A : DependencyObject
{
public static readonly DependencyProperty NameProperty = DependencyProperty.Register(
"Name", typeof(string), typeof(A));
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
}
class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!(value is bool))
{
return Binding.DoNothing;
}
return (bool)value ? Visibility.Visible : Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
有一些代码隐藏来处理我无法在标记中解决的问题。特别是,代码隐藏包含一个事件处理程序,只要 ListBoxItem
获得焦点就会触发该事件处理程序。发生这种情况时,事件处理程序会更新每个 ListBox
的 Tag
属性,以便将包含 ListBoxItem
的那个设置为 true
,而另外两个设置为 false
.
这样,我用Tag
属性作为ListBoxItem
IsFocused
属性的代理,让我的TextBox
元素将它们的 Visibility
属性绑定到它。请注意,我仅在 收到 焦点时设置 Tag
。这样,可见性保持设置,即使当用户 selects TextBox
元素失去焦点时也是如此。
当您 运行 这个程序时,每个 ListBox
只用一个项目初始化。当您 select 一个项目时,它会显示在右侧的 TextBox
中。随着三个 ListBox
元素及其项之间的焦点变化,显示和隐藏不同的 TextBox
元素,但对用户而言,它看起来就像正在更新单个 TextBox
以反映当前 select离子.
如果您编辑 TextBox
中的文本,链接项目的 Name
属性 会像您一样更新。这种变化当然反映在 ListBox
.
我不能向您保证这是最好的 方法。但它确实有效。我对此最大的抱怨是 Tag
属性 的劫持。但事实是,只要您不将 属性 用于其他用途,将它用于此目的可能没问题。
我实际上希望一些真正的 WPF 专家会出现并告诉我我是个傻瓜,并且有更好的方法来实现这一点。但与此同时,我希望以上内容能够充分满足您的需要。 :)
我在上面提到,另一种方法是更新单个 TextBox
元素的 Binding
。我没有显示代码,但不会有太大的不同。你当然只有一个 TextBox
,GotFocus
事件处理程序不会与 ListBox.Tag
属性 混淆,而是检索 GotFocus
的实际数据对象=22=] 发送事件,并相应地为单个 TextBox
对象配置一个新的 Binding
对象。
该方法将避免使用 Tag
属性,甚至可能稍微更有效(或者可能不是……它可以采用任何一种方式)。我个人发现操纵绑定的代码比上面仅设置 属性 值的代码复杂一些。但是由于需要沿着可视化树向上移动以找到父级 ListBox
,因此该代码变得复杂。所以也许是洗了。
除了这些差异之外,代码的结构仍然大同小异。