Andrey Bushman
Andrey Bushman

Reputation: 12476

WPF: Binding for ListBox and ComboBox

I learn bindings in WPF via book. I have wrote such code:

using System;

namespace WpfBinding {
    enum SomeColors {
        Red,
        Green,
        Blue,
        Gray
    }
}

and

using System;

namespace WpfBinding {
    class TestItem {
        SomeColors color;

        public TestItem(SomeColors color) {
            Color = color;
        }
        internal SomeColors Color {
            get { return color; }
            set { color = value; }
        }
        public override string ToString() {
            return Color.ToString();
        }
    }
}

XAML of my Window:

<Window x:Class="WpfBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <ListBox x:Name="listBox" HorizontalAlignment="Stretch" 
                 VerticalAlignment="Stretch" Margin="5"/>
        <ComboBox x:Name="comboBox" HorizontalAlignment="Stretch" 
                  VerticalAlignment="Top" Margin="5" Grid.Column="1"/>
    </Grid>
</Window>

I have tried create binding through code:

using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfBinding {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window {
        public MainWindow() {
            InitializeComponent();
            // Data for listbox
            TestItem[] items = new TestItem[] {
                new TestItem(SomeColors.Red), 
                new TestItem(SomeColors.Green), 
                new TestItem(SomeColors.Green), 
                new TestItem(SomeColors.Red), 
                new TestItem(SomeColors.Blue), 
                new TestItem(SomeColors.Red), 
            };
            // Create ObservableCollection item
            ObservableCollection<TestItem> collection = new ObservableCollection<TestItem>(items);
            listBox.ItemsSource = collection;// set data for listbox

            comboBox.ItemsSource = Enum.GetValues(typeof(SomeColors)); // Get items from my enum

            // Create bindings
            Binding bind = new Binding();
            bind.Source = listBox;
            bind.Path = new PropertyPath("SelectedItem.Color");
            bind.Mode = BindingMode.TwoWay;
           comboBox.SetBinding(ComboBox.SelectedItemProperty, bind);
        }
    }
}

But my binding ain't working. Why?

Screen: enter image description here

Upvotes: 0

Views: 2283

Answers (4)

Andrey Bushman
Andrey Bushman

Reputation: 12476

Thanks all. I have edit my code:

using System;
using System.ComponentModel;

namespace WpfBinding {
    public class TestItem : INotifyPropertyChanged{
        SomeColors color;

        public TestItem(SomeColors color) {
            Color = color;
        }
        public SomeColors Color {
            get { return color; }
            set { color = value;
            OnPropertyChanged("Color");
                 }
        }
        public override string ToString() {
            return Color.ToString();
        }

        void OnPropertyChanged(String name) {
            PropertyChangedEventHandler temp = PropertyChanged;
            if (null != temp) {
                temp(this, new PropertyChangedEventArgs(name));
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
}

and

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfBinding {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window {
        public MainWindow() {
            InitializeComponent();
            // Data for listbox
            TestItem[] items = new TestItem[] {
                new TestItem(SomeColors.Red), 
                new TestItem(SomeColors.Green), 
                new TestItem(SomeColors.Green), 
                new TestItem(SomeColors.Red), 
                new TestItem(SomeColors.Blue), 
                new TestItem(SomeColors.Red), 
            };
            // Create ObservableCollection item
            ObservableCollection<TestItem> collection = new ObservableCollection<TestItem>(items);
            listBox.ItemsSource = collection;// set data for listbox

            ObservableCollection<SomeColors> collection2 = new 
                ObservableCollection<SomeColors>(Enum.GetValues(typeof(SomeColors)).Cast<SomeColors>());
            comboBox.ItemsSource = collection2; // Get items from my enum
            // Create bindings
            Binding bind = new Binding();
            bind.Source = listBox;
            bind.Path = new PropertyPath("SelectedItem.Color");
            bind.Mode = BindingMode.TwoWay;
           comboBox.SetBinding(ComboBox.SelectedItemProperty, bind);
        }
    }
}

Look Screen, please: enter image description here

Upvotes: 1

Rohit Vats
Rohit Vats

Reputation: 81243

Here is the error -

System.Windows.Data Error: 40 : BindingExpression path error: 'Color' property
not found on 'object' ''TestItem' (HashCode=13974362)'.  
BindingExpression:Path=SelectedItem.Color; DataItem='ListBox' (Name='listBox');
target element is 'ComboBox' (Name='comboBox'); target property is 'SelectedItem'
(type 'Object')

You need to make the property Color public instead of internal.

From MSDN here -

The properties you use as binding source properties for a binding must be public properties of your class. Explicitly defined interface properties cannot be accessed for binding purposes, nor can protected, private, internal, or virtual properties that have no base implementation.

Upvotes: 2

CodingGorilla
CodingGorilla

Reputation: 19842

I think the problem is that your classes are not implementing INotifyPropertyChanged.

In order for the bindings to know when a property has changed it's value, you have to send it notification, and you do that with INotifyPropertyChanged.

UPDATE

So your listbox is bound to an ObservableCollection which does provide change notifiations, but only to the list box and only if you add or remove items from the collections.

You might also want to enable WPF Binding trace information in visual studio (http://msdn.microsoft.com/en-us/library/dd409960%28v=vs.100%29.aspx) that might help you figure out what is going on too.

The last thing I noticed is that the Color property of your TestItem class is marked as internal. WPF won't have access to that property unless it's public.

Upvotes: 2

skink
skink

Reputation: 5711

It's always useful to watch the Output window of Visual Studio when debugging! Had you looked there, you'd have seen this:

System.Windows.Data Error: 40 : BindingExpression path error: 'Color' property not found on 'object' ''TestItem' (HashCode=20856310)'. BindingExpression:Path=SelectedItem.Color; DataItem='ListBox' (Name='listBox'); target element is 'ComboBox' (Name='comboBox'); target property is 'SelectedItem' (type 'Object')

Exactly, binding can be done with public properties only, so

internal SomeColors Color

should be

public SomeColors Color

Upvotes: 2

Related Questions