0

I have a MainWindow with a TextBox and a Listbox. The TextBox is bound two-way to a ListBox, which ist populated from a BindingList.

InitializeComponent();
productNames = new BindingList<string>(/*some content*/);

Binding binding = new Binding();
binding.Mode = BindingMode.TwoWay;
binding.Path = new PropertyPath("SelectedItem");
binding.ElementName = "listBox1";

textBox1.SetBinding(TextBox.TextProperty, binding);

listBox1.ItemsSource = productNames;

So far, so good: The ListBox displays the content of my list nicely, and the TextBox dutifully displays any item that is selected in the ListBox. however, when I type some Text in the Textbox, the corresponding item in the ListBox does not change.

I googled a lot concerning UpdateSourceTrigger, binding, etc., but almost all of it is about using XAML, and nothing I found fits the bill. What am I doing wrong?

H.B.
  • 166,899
  • 29
  • 327
  • 400
HerrMetik
  • 63
  • 1
  • 7
  • Have you tried setting `binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;` in code behind? – DNKROZ Aug 25 '16 at 12:26
  • 1
    I have. It didn't do the trick. I think the answers by Peter and unkreativ explained why not. – HerrMetik Aug 26 '16 at 08:53

2 Answers2

1

This won't work because you are binding to a string. So there is no INotifyPropertyChanged-events fired and so the list does not noticed it got updated. Try to bind the list to some complex object with a string:

class CompexObject : INotifyPropertyChanged
{
    private string myString;

    public event PropertyChangedEventHandler PropertyChanged;

    public string MyString
    {
        get { return this.myString; }
        set
        {
            this.myString = value;
            this.PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(nameof(MyString)));
        }
    }
}

And then set your list like:

 productNames = new BindingList<ComplexObject>(/*some content*/);

Your binding should change to:

Binding binding = new Binding();
binding.Mode = BindingMode.TwoWay;
binding.Path = new PropertyPath("SelectedItem.MyString");
binding.ElementName = "listBox1";

textBox1.SetBinding(TextBox.TextProperty, binding);
unkreativ
  • 482
  • 2
  • 8
  • Thank you very much. This does exactly what I head in mind - with one slight flaw; adding the objects to the list will result in having the type of the object displayed in the listbox itself instead of the String that I intended. Luckiliy, that is easily fixed by adding `listBox1.DisplayMemberPath = "MyString";` – HerrMetik Aug 26 '16 at 08:55
0

You problem is that you dont tell your UI that the string has changed. You can do this with this interface INotifyPropertyChanged. Check out this answer if you are interested and maybe get into MVVM.

But for a simple solution in the code behind i would recommend dependency propertys and Bindings in XAML like in this example.

XAML:

<Window x:Class="Test.MainWindow"
    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"
    xmlns:local="clr-namespace:Test"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525"
    Name="myWindow">
<StackPanel>
    <ListBox ItemsSource="{Binding ElementName=myWindow, Path=ItemCollection, Mode=OneWay}"
             SelectedItem="{Binding ElementName=myWindow, Path=SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">


    </ListBox>


    <TextBox Text="{Binding ElementName=myWindow, Path=TextBoxText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>

Code behind:

namespace Test
{

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        /// <summary>
        /// Store your ListBox entrys here
        /// </summary>
        public ObservableCollection<string> ItemCollection
        {
            get { return (ObservableCollection<string>)GetValue(ItemCollectionProperty); }
            set { SetValue(ItemCollectionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ItemCollection.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ItemCollectionProperty =
            DependencyProperty.Register("ItemCollection", typeof(ObservableCollection<string>), typeof(MainWindow), new PropertyMetadata(new ObservableCollection<string>()));


        /// <summary>
        /// This binds to the TextBox text
        /// </summary>
        public string TextBoxText
        {
            get { return (string)GetValue(TextBoxTextProperty); }
            set { SetValue(TextBoxTextProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TextBoxText.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TextBoxTextProperty =
            DependencyProperty.Register("TextBoxText", typeof(string), typeof(MainWindow), new PropertyMetadata(string.Empty, OnTextBoxTextChanged));

        private static void OnTextBoxTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
        {
            MainWindow self = d as MainWindow;

            if(self != null)
            {
                self.OnTextBoxTextChanged(args.NewValue.ToString());
            }
        }

        private void OnTextBoxTextChanged(string newValue)
        {
            if (this.ItemCollection.Contains(newValue))
            {
                this.SelectedItem = newValue;
            }
            else
            {
                this.SelectedItem = null;
            }
        }


        /// <summary>
        /// This binds to the selected item of your ListBox
        /// </summary>
        public string SelectedItem
        {
            get { return (string)GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectedItem.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register("SelectedItem", typeof(string), typeof(MainWindow), new PropertyMetadata(string.Empty, OnSelectedItemChanged));

        private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
        {
            MainWindow self = d as MainWindow;

            if (self != null)
            {
                self.OnSelectedItemChanged(args.NewValue as string);
            }
        }

        private void OnSelectedItemChanged(string newValue)
        {
            if (!this.TextBoxText.Equals(newValue) && !string.IsNullOrEmpty(newValue))
            {
                this.TextBoxText = newValue;
            }
        }


        public MainWindow()
        {
            InitializeComponent();
            this.ItemCollection.Add("Name 1");
            this.ItemCollection.Add("Name 2");
            this.ItemCollection.Add("Name 3");
            this.ItemCollection.Add("Name 4");
            this.ItemCollection.Add("Name 5");

        }

    }
}

It also unselects the ListBox entry when the TextBox text doesn't match an item from the 'ItemsSource' of the ListBox.

EDIT: More Comments

Community
  • 1
  • 1
Peter
  • 1,655
  • 22
  • 44