Fred Jand
Fred Jand

Reputation: 699

SelectedItem on ComboBox

There is a ComboBox in the application which is bound to a collection of items. There are cases that user can select an item from the ComboBox but the selected item might not be ready yet so the ComboBox selected item must get back to the previous selected item (or some other item in the collection), but in the current application ComboBox always shows the selected item from the user instead of retrieving the valid item after setting it back and calling notify property change.

The flowing is a simplified code of which shows the problem.

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private List<Customer> _Customers = new List<Customer>();

    public List<string> CustomerNames
    {
        get
        {
            var list = new List<string>();
            foreach (var c in _Customers)
            {
                list.Add(c.Name);
            }
            return list; ;
        }
    }

    public string CustomerName
    {
        get
        {
            var customer = _Customers.Where(c => c.IsReady).FirstOrDefault();
            return customer.Name;
        }
        set
        {
            NotifyPropertyChanged("CustomerName");
        }
    }

    public MainWindow()
    {
        SetupCustomers();
        InitializeComponent();
        this.DataContext = this;
    }

    private void SetupCustomers()
    {
        _Customers.Add(new Customer("c1", true));
        _Customers.Add(new Customer("c2", false));
        _Customers.Add(new Customer("c3", false));
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class Customer
{
    public Customer(string name, bool isReady)
    {
        this.Name = name;
        this.IsReady = isReady;
    }

    public bool IsReady { get; set; }

    public string Name { get; set; }

    public override bool Equals(object obj)
    {
        return base.Equals(obj);
    }
    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}    


<Window x:Class="TryComboboxReset.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>

    <ComboBox   Width="100"
                Height="25"
                ItemsSource="{Binding Path=CustomerNames, Mode=OneWay}"
                SelectedItem="{Binding Path=CustomerName, Mode=TwoWay}"/>

</Grid>

Upvotes: 1

Views: 1219

Answers (2)

Fred Jand
Fred Jand

Reputation: 699

The problem was UI thread, I used dispatcher to fix this problem

 public partial class MainWindow : Window, INotifyPropertyChanged
{
    private ObservableCollection<Customer> _Customers =
        new ObservableCollection<Customer>();

    public ObservableCollection<Customer> CustomerNames
    {
        get
        {
            return _Customers;
        }
    }

    public Customer CustomerName
    {
        get
        {
            return _Customers.Where(c => c.IsReady == true).FirstOrDefault();
        }
        set
        {
            // Delay the revert
            Application.Current.Dispatcher.BeginInvoke(
                new Action(() => NotifyPropertyChanged("CustomerName")), DispatcherPriority.ContextIdle, null);
        }
    }

    public MainWindow()
    {
        SetupCustomers();
        InitializeComponent();
        this.DataContext = this;
    }

    private void SetupCustomers()
    {
        _Customers.Add(new Customer("c1", true));
        _Customers.Add(new Customer("c2", false));
        _Customers.Add(new Customer("c3", false));
        CustomerName = _Customers.Where(c => c.IsReady == true).FirstOrDefault();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class Customer
{
    public Customer(string name, bool isReady)
    {
        this.Name = name;
        this.IsReady = isReady;
    }

    public bool IsReady { get; set; }

    public string Name { get; set; }
}   

<ComboBox   Width="400"
            Height="25"
            ItemsSource="{Binding Path=CustomerNames}"
            SelectedValue="{Binding CustomerName,Mode=TwoWay}"    >
       <ComboBox.ItemTemplate>
             <DataTemplate>
                 <TextBlock Text="{Binding Name}"/>
             </DataTemplate>
        </ComboBox.ItemTemplate>
 </ComboBox>

Upvotes: 1

Tom Studee
Tom Studee

Reputation: 10452

You aren't actually setting the value of the selected customer name in your setter, and your getter is always going to return the first "ready" customer name it finds...

If I'm understanding the problem correctly, you need to be doing something more along these lines:

private string _customerName = null;

public string CustomerName
    {
        get
        {
            if(_customerName == null) 
            {
                _customerName = _Customers.Where(c => c.IsReady).FirstOrDefault().Name;
            }
            return _customerName;
        }
        set
        {
            _customerName = value;
            NotifyPropertyChanged("CustomerName");
        }
    }

Upvotes: 0

Related Questions