Search code examples
c#wpfwpfdatagrid

Update TextBox text when a ComboBox's value is changed


My app: A Store has a Products collection and an int Id property. A Product has string ProductCode and Description properties. A ViewModel references a single instance of Store. ViewModel has a static collection of Products (ProductList). My View's DataContext is set to ViewModel. My View displays Store via the ViewModel.

What works: My View has a TextBlock bound to Store's Id.

What doesn't work: My View has a DataGrid for adding Products to Store's Products collection. The DataGrid for adding Products to Store's Products collection allows me to choose a new ProductCode, using a DataGridComboBoxColumn column. This works fine. However, I want my chosen ProductCode to update the DataGridTextBoxColumn which is bound to a Product's Description.

I've spent hours searching the net and I haven't found anything that quite matches my scenario, except maybe "Example 12" from this link, but I haven't gotten it to work for my project: Best ComboBox Tutorial Ever

Solved:

The most significant change to my code is a RefreshDescription method that fires after ProductCode is set for a Product. This method takes ProductCode as an argument and queries a static ProductList to find the first matching Description. The user can change the auto-populated Description property if desired.

Code snippet from my view:

<StackPanel>
    <TextBlock Text="{Binding Store.Id}"/>
    <DataGrid ItemsSource="{Binding Store.Products}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridComboBoxColumn 
                Header="ProductCode"
                ItemsSource="{x:Static m:ItemsProvider.ProductList}" 
                SelectedValueBinding="{Binding ProductCode,  UpdateSourceTrigger=PropertyChanged}" 
                SelectedValuePath="ProductCode"
                DisplayMemberPath="ProductCode"/>
        <!-- 
        I want Description for my chosen Product to pop in automatically... but how? 
        -->
            <DataGridTextColumn Header="Description" Binding="{Binding Description}"/>
        </DataGrid.Columns>
    </DataGrid>
</StackPanel>

My ViewModel:

public class ViewModel : NotifyObject
{
    // Constructor
    public ViewModel()
    {
        _store = new Store() { Id = 1 };
    }
    
    // Fields
    Store _store;
    Product _selectedProduct;
    
    // Properties
    public Store Store {
        get { return _store; }
        set {
            _store = value;
            base.NotifyPropertyChanged("Commission");
        }
    }   
}

My Product model:

public class Product : NotifyObject
{
    // Constructor
    public Product() { }
    
    // Fields
    string _productCode;
    string _description;
    
    // Properties
    public string ProductCode { 
        get { return _productCode; }
        set {
            _productCode = value;       
            base.NotifyPropertyChanged("ProductCode");
            RefreshDescription(ProductCode);
        }
    }
    
    public string Description {
        get { return _description; }
        set {
            _description = value;
            base.NotifyPropertyChanged("Description");
        }
    }
    
   // Private Methods
    void RefreshDescription(string productCode)
    {
        if (ItemsProvider.ProductList.Count == 0) {
            return;
        }
        Product product = ItemsProvider.ProductList.FirstOrDefault(p => p.ProductCode == productCode);
        this.Description = (product == null ? "" : product.Description);
    }
}

My Store model:

public class Store : NotifyObject
{
    // Constructor
    public Store()
    {
        Products = new ObservableCollection<Product>();
    }
    
    // Fields
    int _id;
    
    // Properties       
    public int Id { 
        get { return _id; }
        set {
            _id = value;
            base.NotifyPropertyChanged("Id");
        }
    }
    
    public ObservableCollection<Product> Products { get; set; }     
}

My static class for getting a list of products to choose from:

public static class ItemsProvider
{
    static ObservableCollection<Product> _productList = new ObservableCollection<Product>();
    static ItemsProvider() 
    {
        _productList = new ObservableCollection<Product>() {
            new Product() { ProductCode = "111", Description = "a" },
            new Product() { ProductCode = "222", Description = "b" },
            new Product() { ProductCode = "333", Description = "c" },
            new Product() { ProductCode = "444", Description = "d" }                    
        };
    }
    public static ObservableCollection<Product> ProductList {
        get {
            return _productList;
        }           
    }           
}

Solution

  • From my edited question:

    The most significant change to my code is a RefreshDescription method that fires after ProductCode is set for a Product. This method takes ProductCode as an argument and queries a static ProductList to find the first matching Description. The user can change the auto-populated Description property if desired.

    Thank you again for those who helped me to come to this conclusion!