WPF cascading comboboxes with LINQ - c#

I do have a number of combo boxes on a custom control as below
<Label Grid.Row="12" Grid.Column="0" Name="lblCombobox1">
Select value from Combobox1
</Label>
<ComboBox Grid.Row="12" Grid.Column="1" Name="cbxCombobox1"
SelectionChanged="cbxCostCentre_SelectionChanged" />
<Label Grid.Row="13" Grid.Column="0" Name="lblCombobox2">
Select value from Combobox2</Label>
<ComboBox Grid.Row="13" Grid.Column="1" Name="cbxCombobox2"/>
This custom control I’m using on a main window as below
<StackPanel Background="LightCyan">
<views:NewAccount HorizontalAlignment="Center"
Margin="30" FontSize="14"/>
I’d need to do populate combo boxes cascading to have a previous combo box selected value as a filter parameter for the next one.
It seems as the following code could do it. It’s doing the filtering providing the Combobox2 with a list of values. However, I’m probably missing something as the LINQ query with “where” clause provides a result that is different if I ran it with T-SQL. It’s very similar but a few more or less values in the Combobox2 that is different from T-SQL list.
using System.Linq;
namespace AccountsSetup.UserControls
{
public partial class NewAccount : UserControl
{
public NewAccount()
{
InitializeComponent();
using (SQL.DBDataContext db = new SQL.DBDataContext())
{
var allCombobox1s = from t in db.Table1
select t.Name;
cbxCombobox1.ItemsSource = allCombobox1s;
}
}
private void cbxCombobox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
using (SQL.DBDataContext dbs = new SQL.DBDataContext())
{
string value = "";
if (cbxCombobox1.SelectedIndex >= 0)
value = cbxCombobox1.SelectionBoxItem.ToString();
var allCombobox2s = (from t in dbs.View1
where t.Combobox1.Contains(value)
select t.Name).Distinct();
cbxCombobox2.ItemsSource = allCombobox2s;
}
I did try to change the Combobox2 into the following code. But, it’s the same result.
<ComboBox Grid.Row="13" Grid.Column="1" x:Name="cbxCombobox2"
ItemsSource="{Binding}" SelectedValue="{Binding ElementName=cbxCombobox1,
Path=SelectedItem.Name, Mode=OneWay}" />
Please, advise what corrections could be done in the code.
Thanks

You should compare the value of the SelectedItem property of the first ComboBox with the value of the Name column in the view.
Try this:
private void cbxCombobox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
using (SQL.DBDataContext dbs = new SQL.DBDataContext())
{
string value = cbxCombobox1.SelectedItem as string;
if (!string.IsNullOrEmpty(value))
{
var allCombobox2s = (from t in dbs.View1
where t.Name != null && t.Name.Contains(value)
select t.Name).Distinct().ToList();
cbxCombobox2.ItemsSource = allCombobox2s;
}
}
}

Related

selected value cannot be set

I have a combobox which gets its Items from some scan function.
If the user select an element, in the next time, the user's chosen item should be selected (if it is present on the scan function output). The problem is that I cannot select it.
Here is the declaration of the ComboBox:
<ComboBox Grid.Column="1" Grid.Row="0" Margin="5" Name="SerialPortNames" Text="{Binding Name}" IsEditable="False"/>
and here what I have tried so far:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
string portNameSetting = Settings.Default["SerialPortName"].ToString();
SerialPortNames.ItemsSource = SerialPort.GetPortNames();
foreach (string SerialPortNameItem in SerialPortNames.Items)
{
if (SerialPortNameItem == portNameSetting)
{
SerialPortNames.Text = SerialPortNameItem; // why this is not working
break;
}
}
}
by debugging this, I get the item selected in the combobox, but it seems that something override it and it is empty!
In your code you Binded the Text propery and also setting it from code behind
Remove Text="{Binding Name}" from the combobox
<ComboBox Width="200" Height="200" Grid.Column="1" Grid.Row="0" Margin="5" Name="SerialPortNames" IsEditable="False"/>

How to display multiple checked items in multiselect combobox in wpf?

My Xaml code
<ComboBox HorizontalContentAlignment="Left"
x:Name="Stat"
IsEditable="True"
Width="247"
HorizontalAlignment="Left"
IsReadOnly="True"
ItemsSource="{Binding OrderStatus,Mode=TwoWay}"
Text="{Binding StatusSelectedValue}"
Height="26">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Description}"
IsChecked="{Binding IsChecked}"
Checked="CheckBox_Checked"
Unchecked="CheckBox_Unchecked"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
xaml.cs
//I would like to update the text displayed on my multi-select combobox when items are checked or unchecked
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
var selectedItem = sender as CheckBox ;
var r= selectedNode.IsChecked.Value;
//trying to obtain value of all checked items and concatenate it and display in combobox text something similar like below
Stat.Text = String.Join(",",selectedItemValues.toArray());//this line of code is for example only for what i want to do
}
any help either mvvm way or xaml.cs way is greatly appreciated.
public void SetText()
{
if (this.SelectedItems != null)
{
StringBuilder displayText = new StringBuilder();
foreach (ComboItem s in comboItemsCollection)
{
if (s.IsSelected == true && s.Title == Properties.Resources.CHECKALL)
{
displayText = new StringBuilder();
displayText.Append("All");
break;
}
else if (s.IsSelected == true && s.Title != Properties.Resources.CHECKALL)
{
displayText.Append(s.Title);
displayText.Append(',');
}
}
this.Text = displayText.ToString().TrimEnd(new char[] { ',' });
}
// set DefaultText if nothing else selected
if (string.IsNullOrEmpty(this.Text))
{
this.Text = this.DefaultText;
}
}
This is what i did. It display all the selected items with a ";" as a separator. If you have an "All" checkbox or you select all, it just display All. Of course i don't know what object you are using so i left my code as it is (you need to change the loop part, in particular the ComboItem part)
Note that this is a function (of course), so you can either add directly the code where you are doing the String.Join, or you call it

wpf c# static combobox selection needs to update second combobox that makes a DB call

I'm new to WPF, using DevExpress MVVM, I have a static combobox that when the user selects a day, I want to update the second combobox by passing an int day and making a DB call to refresh that combo box with routes based off that day. Here's what I have:
-->
<Label Content="Route:" Grid.Column="2" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="9,60,0,0" Name="lblRoute" VerticalAlignment="Top" />
<ComboBox Grid.Column="2" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="127,65,0,0" Name="cboRoute" VerticalAlignment="Top" Width="120"
DataContext="{Binding}" ItemsSource="{Binding Path=RouteList}" DisplayMemberPath="{Binding RouteName}" SelectedValue="{Binding RouteID}" Grid.ColumnSpan="2"
IsSynchronizedWithCurrentItem="true"/>
ViewModel:
public ObservableCollection<RouteTest> BindRouteComboBox2(int day)
{
mgr = new SRMSRailManagerBLL();
mgr.OpenConnection(ConfigurationManager.ConnectionStrings["SRMSSqlProvider"].ConnectionString);
//might not want to pass dataset
_routeDS = mgr.getRoutesForCombo(day);
_routeDV = _routeDS.Tables[0].DefaultView;
//cboRoute.ItemsSource = ds.Tables[0].DefaultView;
//cboRoute.DisplayMemberPath = ds.Tables[0].Columns["RouteName"].ToString();
//cboRoute.SelectedValuePath = ds.Tables[0].Columns["RouteID"].ToString();
_routeList = new ObservableCollection<RouteTest>();
foreach (DataRow row in _routeDV.Table.Rows)
{
RouteTest r = new RouteTest(Convert.ToInt32(row.ItemArray[0]), row.ItemArray[1].ToString());
_routeList.Add(r);
}
mgr.CloseConnection();
return _routeList;
When I click my first combobox, I get the 'Monday' populated in my _SelectionChanged(), what I want is the Tag value of '1', so that I can pass to my BindRouteComboBox(day) procedure to update the second combo box to only show routes for day 1, instead of every route in my DB table. This should be simple and I've gotta be missing something simple here.
Thanks,
Hi use SelectedValue instead of SelectedValuePath
string temp = (sender as ComboBox).SelectedValue.ToString();

Bound ItemsControl not updating before displaying

I have a UserControl that is comprised of a few bound ItemsControl's and strings, and based on the button that is pressed, different data is displayed. Here is an example of one of the Button's click events:
private void LeftPreviousScoresButton_Click(object sender, RoutedEventArgs e)
{
if (m_previousScoresWindow.Visibility == Visibility.Visible)
{
m_previousScoresWindow.Hide();
}
else
{
WindowTitle = "Left Side";
PreviousScoresA = m_previousLeftWristErosionScoresReaderA;
PreviousScoresB = m_previousLeftWristErosionScoresReaderB;
m_previousScoresWindow.Show();
}
}
There are several of these click event listeners which assigns WindowTitle, PreviousScoresA, and PreviousScoresB with the associated data. The UserControl then binds to them like this:
<ItemsControl Height="Auto" Width="Auto"
ItemsSource="{Binding ElementName=ParentForm, Path=PreviousScoresA}"
Grid.Row="1" />
<ItemsControl Height="Auto" Width="Auto"
ItemsSource="{Binding ElementName=ParentForm, Path=PreviousScoresB}"
Grid.Row="2" />
<TextBlock FontSize="16" FontWeight="Bold" Height="25"
Margin="5" HorizontalAlignment="Center" Foreground="Black"
Text="{Binding ElementName=ParentForm, Path=PreviousScoresWindowTitle}" />
However, when opening the window, the old data displays for a second before it is updated with the current data. I've even tried adding these calls when calling Hide() on the Window but it didn't seem to help:
WindowTitle = String.Empty;
PreviousScoresA = new ObservableCollection<PreviousScoreData>();
PreviousScoresB = new ObservableCollection<PreviousScoreData>();
Is there any way to ensure that Show() is not called until after the bound data has been updated? Thanks.
As it appears you are using an ObservableCollection, the collection should never be re-initialized. Rather, it should just be cleared and then add the new values; this is what helps keep the collection synchronized when using an ObservableCollection.
This is a bit of a shot in the dark based on your code sample; if you clear the collection when hiding and then refill them with the new values, then you should get the desired effect:
private void LeftPreviousScoresButton_Click(object sender, RoutedEventArgs e)
{
if (m_previousScoresWindow.Visibility == Visibility.Visible)
{
m_previousScoresWindow.Hide();
WindowTitle = string.Empty;
PreviousScoresA.Clear();
PreviousScoresB.Clear();
}
else
{
WindowTitle = "Left Side";
// do not re-initialize the collection; clear and add new values
// PreviousScoresA = m_previousLeftWristErosionScoresReaderA;
// PreviousScoresB = m_previousLeftWristErosionScoresReaderB;
ReFillScores(PreviousScoresA, m_previousLeftWristErosionScoresReaderA);
ReFillScores(PreviousScoresB, m_previousLeftWristErosionScoresReaderB);
m_previousScoresWindow.Show();
}
}
private void ReFillScores (ObservableCollection<PreviousScoreData> collection, IEnumerable<PreviousScoreData> values)
{
collection.Clear();
foreach(PreviousScoreData d in values)
{
collection.Add(d);
}
}

WPF selected value , displaymember path inconsistency with listbox and combo box

I have a stored procedure called DropDownIndividuals() which was created using LINQ.
The stored procedure returns FullName and Case_Number. I want to set the SelectedValuePath equal to the Case_Number column in my stored procedure. This is how I did it for a listbox and it works.
private void listBox1_Loaded(object sender, RoutedEventArgs e){
using (ToolboxDataContext toolboxDB = new ToolboxDataContext())//this is linq in action
{
var x = toolboxDB.DropDownIndividuals().ToList();//convert to a list
listBox1.ItemsSource = x; //bind the data
listBox1.SelectedValuePath = "Case_Number";
listBox1.DisplayMemberPath = "FullName";
Console.WriteLine(listBox1.SelectedValue.ToString());
//Result:it shows the case number of the person the user picks.
}
}
Now I do the same thing for a dropdown combobox AND IT DOES NOT WORK.
private void individualDropDown_Loaded(object sender, RoutedEventArgs e)
{
using (ToolboxDataContext toolbox = new ToolboxDataContext())
{
var individualDropDownBox = toolbox.DropDownIndividuals().ToList();
individualDropDown.ItemsSource = individualDropDownBox;
individualDropDown.DisplayMemberPath = "FullName";
individualDropDown.SelectedValuePath = "Case_Number";
Console.WriteLine(individualDropDown.SelectedValue.ToString());
}
}
Why? How can I fix this?
Why so chaotic? You do not even set properties in the same order, this is equivalent:
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" Grid.Column="0"
Name="lbData" ItemsSource="{Binding DpData}"
DisplayMemberPath="Name"
SelectedValuePath="Id"/>
<TextBlock Grid.Row="1" Grid.Column="0"
Text="{Binding ElementName=lbData, Path=SelectedValue}"/>
<ComboBox Grid.Row="0" Grid.Column="1" VerticalAlignment="Top"
Name="cbData" ItemsSource="{Binding DpData}"
DisplayMemberPath="Name"
SelectedValuePath="Id"/>
<TextBlock Grid.Row="1" Grid.Column="1"
Text="{Binding ElementName=cbData, Path=SelectedValue}"/>
</Grid>
...and it displays the same ID as expected.
Edit: At startup the selected value of both controls is null by the way.
You are correct, there is an inconsistency of sorts between the way that SelectedValue is treated for ListBox and ComboBox. For ListBox, upon load, if it has the focus, the SelectedValue will correspond to the first item in the data source. For ComboBox even if it has the focus and a data source supplies items, the default SelectedValue will be unset during the Loaded event handler.
This behavior is by design. To make the ComboBox behave like the ListBox set ComboBox.SelectedIndex to "0" where you define the ComboBox in the XAML.
Try this:
MetroAreaList metroAreaList = _presenter.GetMetroArea();
foreach (MetroArea metroArea in metroAreaList) {
lstMetroArea.DisplayMemberPath = "Name";
lstMetroArea.SelectedValuePath = "ID";
lstMetroArea.Items.Add(metroArea);
}
It is working....
Your class should be public:
public class Place
{
public string Name { get; set; }
public string Id { get; set; }
}
foreach (var y in Lists)
{
listBox1.DisplayMemberPath = "Name";
listBox1.SelectedValuePath = "Id";
// Console.WriteLine(y.Case_Number.ToString());
listBox1.Items.Add(y);
}

Categories