【问题标题】:How to bind selectedItem from different listboxes to one textbox?如何将不同列表框中的 selectedItem 绑定到一个文本框?
【发布时间】:2015-07-28 19:11:36
【问题描述】:

我对 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}"/>

现在,当我在 Listbox2 中选择项目时,我想将同一个文本框的值更改为“Brian Warner”,当我在 Listbox3 中选择她的名字时,将同一个文本框更改为“Anne Brown”。 我还希望能够编辑他们的名字并在列表框中进行更新。

我想我需要找到一种方法,每次我在列表框中选择和项目时,将绑定 ElementName 从 Listbox1 更改为 Listbox2,再更改为 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;
       }

但是当我尝试更改文本框中的值时,它不会在列表框中更新。我该怎么做?

【问题讨论】:

    标签: c# wpf xaml binding listbox


    【解决方案1】:

    如果我正确理解了这个问题,您有一个编辑字段(TextBox),您想用它来显示和编辑在ListBox 中选择的数据对象的Name 属性。有多个这样的ListBox 对象,用户应该能够编辑在最近使用的ListBox 中选择的数据对象。

    事实证明,这并不是一件微不足道的事情。我同意 Ben Cohen 的回复,其中一种方法是在焦点更改时显示和隐藏 不同 TextBox 实例。一种相关但不同的方法是只有一个 TextBox 并根据焦点的变化更新其 Binding 属性。

    但它比这更复杂,因为当用户在ListBox 中选择一个项目时,ListBox 本身并不是获得焦点的对象。这是ListBox 中的ListBoxItem。似乎没有一种方便的方法可以在 XAML 中访问所选的 ListBoxItem。但即使你可以,还有另一个问题……

    此外,您不能简单地将每个TextBox 的可见性绑定到焦点状态(或合适的代理),因为一旦用户实际单击TextBoxListBox/its代理失去焦点。

    无论如何,在稍微解决了这个问题(坦率地说,可能比我应该做的要多,但是嘿……我在这里也学到了一些新东西:)),我想出了一个代码示例,我认为它确实可以你在这里要求(我希望这反过来是你想要的:)):

    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 获得焦点,就会触发该事件处理程序。发生这种情况时,事件处理程序会更新每个ListBoxTag 属性,从而将包含ListBoxItem 的属性设置为true,而将其他两个设置为false

    通过这种方式,我使用Tag 属性作为ListBoxItemIsFocused 属性的代理,允许我的TextBox 元素将其Visibility 属性绑定到该属性。请注意,我只在收到焦点时设置Tag。这样,即使在用户选择 TextBox 元素时失去焦点,可见性仍然保持设置。

    当你运行这个程序时,每个ListBox 都被初始化为只有一个项目。当您选择一个项目时,它会显示在右侧的TextBox 中。随着焦点在三个ListBox 元素及其项目之间的变化,不同的TextBox 元素会显示和隐藏,但对用户来说,它看起来就像一个TextBox 正在更新以反映当前的选择。

    如果您编辑TextBox 中的文本,则链接项目的Name 属性会与您一样更新。这种变化当然体现在ListBox的显示上。


    我不能向你保证这是最好的方法。但它确实有效。我最大的抱怨是劫持了Tag 财产。但事实是,只要您不将该属性用于其他任何用途,就可以将其用于此目的。

    我实际上希望一些真正的 WPF 专家会出现并告诉我我是一个假人,并且有更好的方法来实现这一点。但与此同时,我希望以上内容能够充分满足您的需求。 :)


    我在上面提到过,另一种方法是为单个 TextBox 元素更新 Binding。我没有显示代码,但它不会有太大的不同。当然,您只有一个 TextBox,而 GotFocus 事件处理程序不会与 ListBox.Tag 属性混淆,而是会检索 ListBoxItem 发送事件的实际数据对象,并配置一个新的Binding 对象对应于单个 TextBox 对象。

    这种方法将避免使用 Tag 属性,甚至可能会稍微更有效(或者可能不会……它可以采用任何一种方式)。我个人发现操作绑定的代码比上面只设置属性值的代码要复杂一些。但是由于需要沿着可视化树查找父级ListBox,因此代码变得复杂。所以也许是洗了。

    除了这些差异之外,代码的结构仍然大致相同。

    【讨论】:

      【解决方案2】:

      一个简单的解决方案可能是

      1- 定义一个保存列表框引用的变量 2-对所有三个使用 OnItemSelectionChange 事件 3-更新标志以指示调用哪个控制事件 4-从每个事件中更改文本框的值 5- 当文本框中的值改变时,使用标志来改变列表框中的值。

      将一个文本框与三个列表框绑定是不合逻辑的。

      【讨论】:

        【解决方案3】:

        好吧,您可以在代码后面等中处理这种绑定。但是,我会实现更简单的解决方案:只需创建 3 个具有相同布局和样式(或不同)的文本框,每个文本框都绑定到特定的列表框,当您引发列表框的 onItemSelected 事件时,只需显示相关的文本框并折叠另一个。瓦拉!没有任何可能导致麻烦的绑定处理。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-07-23
          • 1970-01-01
          • 1970-01-01
          • 2017-02-02
          • 1970-01-01
          • 1970-01-01
          • 2011-02-16
          相关资源
          最近更新 更多