Question

I currently have a Listview box with 3 comboboxes. I am populating them with from a sql database. For each row, I want to have the 3rd combobox change it's contents based on the selected values of the 2nd combobox.

The comboboxes will be: cmbx1 (employee[jack, jill, tom, lisa]), cmbx2(products[pen, pencil, stapler]), cmbx3(color - will be dynamic based on what color is available for the product)

product and color options: pen[red, blue, black]; pencil[black, orange, red]; stapler[pink, teal, purple, brown]

If for Row1, the user selects a pen, then only the available colors for that product will be listed in the color combobox for that row. The next row could have a different color option based on the product selected.

Is this possible or should i find another way to achieve the results?

here's what a currently have...

<ListView.View>
    <GridView>
        <GridViewColumn Header="Employee" Width="150">
            <GridViewColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{Binding lStrEmployee}" Width="120" />
                </DataTemplate>
            </GridViewColumn.CellTemplate>
        </GridViewColumn>
        <GridViewColumn Header="Product" Width="150">
            <GridViewColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{Binding lStrProduct}" Width="120" />
                </DataTemplate>
            </GridViewColumn.CellTemplate>
        </GridViewColumn>
        <GridViewColumn Header="Color" Width="150">
            <GridViewColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{Binding lStrColor}" Width="120" />
                </DataTemplate>
            </GridViewColumn.CellTemplate>
        </GridViewColumn>
</ListView.View>

code behind

List<Int32> liEmployee = new List<Int32>();
List<string> lsEmployee = new List<string>();
List<Int32> liProduct = new List<Int32>();
List<string> lsProduct = new List<string>();
List<Int32> liColor = new List<Int32>();
List<string> lsColor = new List<string>();

SqlConnection conn = new SqlConnection("Data Source=localhost\\SQLEXPRESS;Initial Catalog=testDB;Persist Security Info=True;User ID=USER;Password=PASSWORD;");//Connect Timeout=900
SqlCommand cmd1 = new SqlCommand("select id,employee from testDB.dbo.dmEmployee where inactive=0", conn);
SqlCommand cmd2 = new SqlCommand("select id,Product from testDB.dbo.tblProductList where inactive=0", conn);
SqlCommand cmd3 = new SqlCommand("select id,Color from testDB.dbo.Color where inactive=0", conn);

conn.Open();
SqlDataReader dr1 = cmd1.ExecuteReader();
while (dr1.Read())
{
    liEmployee.Add(dr1.GetInt32(dr1.GetOrdinal("id")));
    lsEmployee.Add(dr1.GetString(dr1.GetOrdinal("employee")));
}
conn.Close();
conn.Open();
SqlDataReader dr2 = cmd2.ExecuteReader();
while (dr2.Read())
{
    liProduct.Add(dr2.GetInt32(dr2.GetOrdinal("id")));
    lsProduct.Add(dr2.GetString(dr2.GetOrdinal("Product")));
}
conn.Close();
conn.Open();
SqlDataReader dr3 = cmd3.ExecuteReader();
while (dr3.Read())
{
    liColor.Add(dr3.GetInt32(dr3.GetOrdinal("id")));
    lsColor.Add(dr3.GetString(dr3.GetOrdinal("Color")));
}
conn.Close();


List<lvItem> itemFound = new List<lvItem>();
itemFound.Clear();
lvItem puzzlePieces;
for (int cnt = 0; cnt < 10; cnt++)
{
    puzzlePieces = new lvItem();

    puzzlePieces.lStrEmployee = lsEmployee;
    puzzlePieces.lStrDatabase = lsDatabase;
    puzzlePieces.lStrProvider = lsProvider;

    itemFound.Add(puzzlePieces);
}
list1.ItemsSource = itemFound;

Thanks!

Was it helpful?

Solution

I'm surprised that you didn't get any answers to your question. Maybe it's because you don't seem to be doing things the WPF way, or maybe because you're asking for so much?

First things first... you need to create a data type class that implements the INotifyPropertyChanged interface and contains all of the properties required for display in each row of the ListView. In your case, you need three collections and three selected item values. As an example, you could do something like this (implementing the INotifyPropertyChanged interface yourself):

public class RowData : INotifyPropertyChanged
{
    public ObservableCollection<Employee> Employees { get; set; }
    public Employee SelectedEmployee { get; set; }
    public ObservableCollection<Product> Products { get; set; }
    public Product SelectedProduct { get; set; }
    public ObservableCollection<Brush> Colours { get; set; }
    public Brush SelectedColour { get; set; }
}

Note the use of the Brush class rather than the Color struct, this is because Brush is a class, which means that we can bind to it and also because it is more predominantly used in WPF.

However, it is not optimal having the same collections in every object in every row, except for the Colours collection, which could be different for each row. Having said that, that is exactly what I'm going to do because it will be quicker for me to explain and you can improve your code yourself at a later stage:

So now you have your data type class, we need to add a property of that type to bind to your ListView control. If you are using the code behind of your MainWindow, then let's create a DependencyProperty for it:

public static readonly DependencyProperty RowDataProperty = DependencyProperty.
    Register("RowData", typeof(ObservableCollection<RowData>), typeof(MainWindow), 
    new UIPropertyMetadata(new ObservableCollection<RowData>()));

public ObservableCollection<RowData> RowData
{
    get { return (ObservableCollection<RowData>)GetValue(RowDataProperty); }
    set { SetValue(RowDataProperty, value); }
}

After filling your collection, you can now bind it to the ListView control:

xmlns:Local="clr-namespace:YourWpfApplicationName"
...
<ListView ItemsSource="{Binding RowData, RelativeSource={RelativeSource AncestorType={
    x:Type Local:MainWindow}}}">
    ...
</ListView>

In short, the RelativeSource Binding is simply looking for the property you defined in the code behind. Now, how to define that a ComboBox should appear in each GridViewColumn? You need to define the GridViewColumn.CellTemplate:

<GridViewColumn Header="Employees">
    <GridViewColumn.CellTemplate>
        <DataTemplate>
            <ComboBox ItemsSource="{Binding Employees}" SelectedItem="{Binding 
                SelectedEmployee}" />
        </DataTemplate>
    </GridViewColumn.CellTemplate>
</GridViewColumn>

You'll need to define the other columns from this example. So the final part of this puzzle is how to update the content of the Colours ComboBox dependent on the selected values of the other ComboBoxes? The answer lies in your selected value properties in your RowData class:

public Employee SelectedEmployee
{
    get { return selectedEmployee; }
    set
    {
        selectedEmployee = value;
        NotifyPropertyChanged(SelectedEmployee);
        Colours = GetColours();
    }
}

private ObservableCollection<Brush> GetColours()
{
    ObservableCollection<Brush> newColours = new ObservableCollection<Brush>();
    if (SelectedEmployee.Name == "Some Name" && SelectedProduct.Name == 
        "Some Product") newColours.AddRange( new List<Brush>() { Brushes.Red, 
        Brushes.White, Brushes.Blue } );
    else ...
}

There are many ways to do this and I'll leave that up to you. You should now have a working example and I now realise why nobody had answered your question... far too much for anyone sane to type! After spending so long on this, I would appreciate it if you try to solve any minor problems you find with this on your own and hope it helps you.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top