changing listBox items dynamically when selection changes in comboBox - c#

I have a comboBox which allows user to choose the selection they want. Based on the selection of the comboBox, i would display the listBox with a list of strings that is related to the user selection.
Example: User chooses "Animals" on comboBox, listBox will display "Monkeys, Horses, Pigs".
Trying to create this simple binding with minimal coding( XAML driven) but to no avail for 1 day. Thanks in advance!
Edit:
Hi for those interested in doing it another way (using only the xaml and a class to store all your data) you could check out the answer by Jehof in the link provided. It is quite a simple way to achieve this.
ListBox does not display the binding data

Here is a quick example of what you are looking for(to get you started).
First create an object that contains all your data and bind that to the ComboBox, the use the Comboboxes SelectedItem to populate the ListBox.
Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Categories.Add(new Category { Name = "Animals", Items = new List<string> { "Dog", "Cat", "Horse" } });
Categories.Add(new Category { Name = "Vehicles", Items = new List<string> { "Car", "Truck", "Boat" } });
}
private ObservableCollection<Category> _categories = new ObservableCollection<Category>();
public ObservableCollection<Category> Categories
{
get { return _categories; }
set { _categories = value; }
}
}
public class Category
{
public string Name { get; set; }
public List<string> Items { get; set; }
}
Xaml:
<Window x:Class="WpfApplication10.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" Name="UI">
<StackPanel DataContext="{Binding ElementName=UI}">
<ComboBox x:Name="combo" ItemsSource="{Binding Categories}" DisplayMemberPath="Name"/>
<ListBox ItemsSource="{Binding SelectedItem.Items, ElementName=combo}"/>
</StackPanel>
</Window>
Result:

Related

Sort ListBox items by Content where ListBoxItem is a StackPanel with children

I have what I believe to be a potentially unique situation.
My ListBox items consist of the following:
StackPanel
Image
ListItem
The ListItem and Image are inserted into the StackPanel, then the StackPanel is the inserted into the ListBox for each item in the array.
Now the challenging part comes in sorting the content by the ListItem's Content (text) as it's a child of the StackPanel. Naturally, the StackPanel does not contain a Content member, so using the below code fails.
this.Items.SortDescriptions.Add(new System.ComponentModel.SortDescription("Content",
System.ComponentModel.ListSortDirection.Ascending));
So I figured, what if I set my StackPanel's data context to my ListItem, then surely it will find it.
stackPanel.DataContext = this.Items;
However, that also fails.
I'm creating my ListItems programatically in the code behind, via data that is loaded in via Json.Net.
My goal here is to sort the items from A-Z, based on the Items Content. I would prefer to keep my current implementation (creating the data programatically) as it gives me more control over the visuals. Plus, it's only about 20 lines of code.
Is it possible to use SortDescriptions when the ListItem's content is a StackPanel ?
Thank you
PS: Only started with WPF today, but have been developing WinForms apps for nearly 2 months.
The WPF way to do it would be to bind your ListBox ItemsSource to an ObservableCollection containing your items.
You would then be able to sort your observableCollection liks so :
CollectionViewSource.GetDefaultView(YourObservableCollection).SortDescriptions.Add(new SortDescription("PropertyToSort", ListSortDirection.Ascending));
Here is a small project that highlights this :
XAML :
<Window x:Class="stackPanelTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:stackPanelTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Image}" />
<TextBlock Text="{Binding Item.Content}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Code Behind :
public partial class MainWindow : Window
{
public ViewModel Items { get; set; } = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
}
ViewModel :
public class ViewModel : ObservableCollection<ListItem>
{
public ViewModel()
{
populateItems();
CollectionViewSource.GetDefaultView(this).SortDescriptions.Add(new SortDescription("Item.Content", ListSortDirection.Ascending));
}
private void populateItems()
{
addOneItem(0, "zero");
addOneItem(1, "one");
addOneItem(2, "two");
addOneItem(3, "three");
addOneItem(4, "four");
}
private void addOneItem(int img, string content)
{
ListItem item = new ListItem();
item.Image = img;
item.Item = new SomeItem { Content = content };
Add(item);
}
}
public class ListItem
{
public int Image { get; set; }
public SomeItem Item { get; set; }
}
public class SomeItem
{
public string Content { get; set; }
}
I took the liberty of renaming your "ListItem" into a "SomeItem" class because I didn't know what it was.
Then I made a "ListItem" class which is used to contain a Image/SomeItem pair (which is what your ListBox is composed of).
Also I used an int instead of an actual image but that should be easily changable.
Here's a screenshot of what I get when executing this code :
Hope this helps, good luck.
PS : if your items values are susceptible to change, don't forget to implement INotifyPropertyChanged in "SomeItem" and "ListItem", otherwise the change won't be updated in your view.
Is it possible to use SortDescriptions when the ListItem's content is a StackPanel ?
No. You will have to implement the sorting logic yourself.
There is no easy way to apply custom sorting to the ItemCollection that is returned from the Items property of the ListBox so instead of adding items to this one you could add the items to a List<StackPanel> and sort this one.
You could still create the data programatically just as before.
Here is an example for you:
Code:
public partial class MainWindow : Window
{
private List<StackPanel> _theItems = new List<StackPanel>();
public MainWindow()
{
InitializeComponent();
//create the items:
StackPanel sp1 = new StackPanel();
ListBoxItem lbi1 = new ListBoxItem() { Content = "b" };
Image img1 = new Image();
sp1.Children.Add(lbi1);
sp1.Children.Add(img1);
_theItems.Add(sp1);
StackPanel sp2 = new StackPanel();
ListBoxItem lbi2 = new ListBoxItem() { Content = "a" };
Image img2 = new Image();
sp2.Children.Add(lbi2);
sp2.Children.Add(img2);
_theItems.Add(sp2);
StackPanel sp3 = new StackPanel();
ListBoxItem lbi3 = new ListBoxItem() { Content = "c" };
Image img3 = new Image();
sp3.Children.Add(lbi3);
sp3.Children.Add(img3);
_theItems.Add(sp3);
//sort the items by the Content property of the ListBoxItem
lb.ItemsSource = _theItems.OrderBy(x => x.Children.OfType<ListBoxItem>().FirstOrDefault().Content.ToString()).ToList();
}
}
XAML:
<ListBox x:Name="lb" />

WPF ComboBox does not display content

I have a simple combo box on my xaml file:
<ComboBox Name="environmentComboBox" Grid.Column="1" Grid.Row="0" Margin="2"
SelectionChanged="environmentComboBox_SelectionChanged"
ItemsSource="{Binding Path=Test}"/>
Here is the code for its content:
private List<string> test = new List<string>(){"1", "2"};
public List<string> Test
{
get
{
return test;
}
set
{
test = value;
}
}
I tried to debug the application, the ComboBox does not show anything.
But when I checked if Test has content, it shows the two strings.
Have to set the views DataContext to the Model/Window containing the List<T>?
If not you need to tell the View what DataContext to use, below is a quick example of a WPF window, and setting the xamls DataContext to the code behind of the View.
Also its recommended to use ObservableCollection<T> when binding collections as adding and removing items will update the ComboBox automatically
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this; // set datacontext
}
private ObservableCollection<string> test = new ObservableCollection<string>() { "1", "2" };
public ObservableCollection<string> Test
{
get { return test; }
set { test = value; }
}
}
<Window x:Class="WpfApplication1.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">
<StackPanel>
<ComboBox ItemsSource="{Binding Path=Test}"/>
</StackPanel>
</Window>

WPF DataGrid with 2 DataGridComboBoxColumns update second combobox based on selection of first combobox

I have a Datagrid that is bound to a datasoruce. Also there are 2 DataGridComboBoxColumn's that are populated by a static List. The binding of the DataGridComboBoxColumn's are like:
<DataGridComboBoxColumn Header="Category" Width="150" SelectedValuePath="ID" SelectedValueBinding="{Binding Category, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="CategoryName" ItemsSource="{Binding ReturnCategories, Source={StaticResource Typeslist}}"/>
<DataGridComboBoxColumn Header="Sub Category" Width="150" SelectedValuePath="ID" SelectedValueBinding="{Binding SubCategory, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="CategoryName" ItemsSource="{Binding ReturnSubCategories, Source={StaticResource Typeslist}}"/>
When the datagrid is loaded the Main Category combobox is populated with the categories and also selected correctly based on the record set.
The second combobox is populated also. What I want to achieve is whenever I change the main category combobox the sub category combobox needs to load corresponding values based on the selection of main categories.
Currently the static resources of the main and sub categories doesn’t accept any parameters. Is it possible to pass a parameter to the sub category static resource so it could load the corresponding list.
The static resources are populated by the database on call from XAML.
static List<MainCategories> mainCatergoryBuffer = new List<MainCategories>();
static List<SubCategories> subCatergoryBuffer = new List<SubCategories>();
If I should change the subcategory content based on main category selection does this mean that the other rows values of the sub category will effected also?
How can I solve this issue?
Grid example:
EDIT
You won't be easily able to do that, these combo boxes do not inherit the data grid data context.
http://msdn.microsoft.com/query/dev12.query?appId=Dev12IDEF1&l=EN-US&k=k(System.Windows.Controls.DataGridComboBoxColumn);k(VS.XamlEditor);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5)&rd=true
Binding in a WPF data grid text column
https://www.google.fr/search?q=Cannot+find+governing+FrameworkElement+or+FrameworkContentElement+for+target+element.&oq=Cannot+find+governing+FrameworkElement+or+FrameworkContentElement+for+target+element.&aqs=chrome..69i57j0l4.1185j0j7&sourceid=chrome&es_sm=122&ie=UTF-8
WPF Error: Cannot find governing FrameworkElement for target element
Advice : use the free data grid from Xceed, you won't run onto such issues
https://wpftoolkit.codeplex.com/
Here's a really simple example on how to achieve this,
Obivously you'll want to adapt to it to your current classes.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
namespace WpfApplication5
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
DataContext = new MyObject();
}
}
internal class MyObject
{
public MyObject()
{
AvailableCategories = new ObservableCollection<Category>(new List<Category>(new[]
{
new Category
{
Name = "category1",
SubCategories = new List<Category>(new[]
{
new Category {Name = "subCategory1a"},
new Category {Name = "subCategory1b"}
})
},
new Category
{
Name = "category2",
SubCategories = new List<Category>(new[]
{
new Category {Name = "subCategory2a"},
new Category {Name = "subCategory2b"}
})
}
}));
}
public ObservableCollection<Category> AvailableCategories { get; private set; }
}
public class Category
{
public string Name { get; set; }
public List<Category> SubCategories { get; set; }
public override string ToString()
{
return String.Format("Name: {0}", Name);
}
}
}
And the XAML :
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wpfApplication5="clr-namespace:WpfApplication5"
Title="MainWindow"
Width="300"
Height="300"
Loaded="Window_Loaded"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="DataTemplateCategory" DataType="wpfApplication5:Category">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</Grid.Resources>
<StackPanel>
<ComboBox x:Name="ComboBox1"
ItemTemplate="{StaticResource DataTemplateCategory}"
ItemsSource="{Binding AvailableCategories}"
d:DataContext="{d:DesignData MyClass}" />
<ComboBox DataContext="{Binding ElementName=ComboBox1,
Path=SelectedItem}"
ItemTemplate="{StaticResource DataTemplateCategory}"
ItemsSource="{Binding Path=SubCategories}"
d:DataContext="{d:DesignData Category}" />
</StackPanel>
</Grid>
</Window>

WPF: Using binding in C#

I'm using a two column (ID,NAME) DataGrid and want update the row with new values.
I'm not sure how I can use the binding part in my C# code.
<DataGrid Name="dataGridUser" ItemsSource="{Binding}" VerticalAlignment="Top" Width="auto" Grid.RowSpan="2"/>
How can I update the datagrid with net values like :
ID , Name
123, Peter
345, Simon
....
So to give you an Example, first create a Model
public class User
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
then in your Code-Behind File create an ObservableCollection of that Model
private ObservableCollection<User> _myUsers;
public ObservableCollection<User> MyUsers
{
get
{
if (_myUsers == null)
{
_myUsers = new ObservableCollection<User>();
}
return _myUsers;
}
}
and now you can bind your DataGrid to this Property
<DataGrid Grid.Row="1" Name="dataGridUser" ItemsSource="{Binding MyUsers}" AutoGenerateColumns="True"/>
and don´t forget to set the DataContext
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"></Window>
if you add a new User to the ObservableCollection MyUsers it will immediately be displayed in your DataGrid, but if you change the FirstName of a existing User it will not display the changes. To do this you must implement INotityPropertyChanged in your Model.
But if you plan to develop a more complex Application I would recommend to take a look at the MVVM-Pattern.
Personally I like the MVVM Light Toolkit this Video should give you a good Idea what MVVM is all about.

I want to display in combobox the items that contain the text I searched for

I got combobox that contain names. My goal is to search with textbox for a string and than the names that start with this string will display in the combobox.
for example:
my combobox contain the next items:
"Mark" , "Arik", "Michael"
when a user will write "Mi" in the textbox, the combobox will display only "Michael".
P.S there is no button. only textbox and combobox.
I prepared you example.
In my example I use two additional assemblies:
Microsoft.Practices.Prism
System.Windows.Interactivity
XAML file:
<Window x:Class="ComboBoxFilter.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"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
>
<Grid>
<StackPanel>
<TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding SearchItems}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<ComboBox ItemsSource="{Binding MySourceData}" />
</StackPanel>
</Grid>
</Window>
ViewModel class:
class MainViewModel : NotificationObject
{
public MainViewModel()
{
_myItems.Add("aaa");
_myItems.Add("abb");
_myItems.Add("aab");
_myItems.Add("bbb");
_myItems.Add("bcc");
_myItems.Add("bbc");
SearchItems = new DelegateCommand(this.OnSearchItems);
}
private string _searchText;
public string SearchText
{
get { return _searchText; }
set { _searchText = value; RaisePropertyChanged(() => SearchText); }
}
private ICollectionView _mySourceData;
public ICollectionView MySourceData
{
get { return CollectionViewSource.GetDefaultView(_myItems); }
}
private List<string> _myItems = new List<string>();
public ICommand SearchItems { get; private set; }
private void OnSearchItems()
{
MySourceData.Filter = (o) => { return string.IsNullOrEmpty(SearchText) ? true : (o as string).StartsWith(SearchText); };
}
}
Do not forget set window DataContext in ctor:
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
Here is all solution with additional assemblies (ComboBoxFilter.zip).
Well, if you use binding to populate the combobox you should just create text property and bind it to the textbox (two way mode binding).
In the setter of this property you just change the filtering condition of the earlier setup filtering for the view.
http://msdn.microsoft.com/en-us/library/system.windows.data.collectionviewsource.getdefaultview.aspx
And in some cases you may wish to filter the collection not its view.
But it's you who decides.

Categories