I have a ListBox and a class with strings. Each time that a user clicks add button in the application, I create a new instance of the class and add it to the list which is binded to the ListBox. The first time I click the add button, the list box shows the first item, but the next time it doesn't show two items.
XAML - this is the ListBox:
<ListBox Name="ListBox_BinsRegion" Height="181" Margin="233,16,6,94" Width="253" Background="Transparent" BorderThickness="1" BorderBrush="Black" ScrollViewer.VerticalScrollBarVisibility="Auto" ItemsSource="{Binding}"/>
The code behind:
List<Class_ListViewItem> List_ListBoxItems = new List<Class_ListViewItem>();
private void Button_Add_Click(object sender, RoutedEventArgs e)
{
Class_ListViewItem item = new Class_ListViewItem();
item.WH = this.comboBox_WareHouseBinsRegionDefinition.SelectedItem.ToString();
item.XXFrom = textBox_XXFrom.Text;
item.XXTo = textBox_XXTo.Text;
item.YYFrom = textBox_YYFrom.Text;
item.YYTo = textBox_YYTO.Text;
item.Z = textBox_ZFrom.Text;
List_ListBoxItems.Add(item);
ListBox_BinsRegion.DataContext = List_ListBoxItems;
}
Where is my mistake?
WPF does not know when your collection is changing. The problem is here:
List<Class_ListViewItem> List_ListBoxItems = new List<Class_ListViewItem>();
you need to change the list to
ObservableCollection<Class_ListViewItem> List_ListBoxItems = new ObservableCollection<Class_ListViewItem>();
ObservableCollection (System.Collections.ObjectModel) throws an event when the collection is changed, so that WPF can update the listbox.
Also, you can remove the following line, or move it to the constructor of your control.
ListBox_BinsRegion.DataContext = List_ListBoxItems;
You should not change the DataContext of the control, instead set the binding to theList_ListBoxItems and make it a public property, and use an ObservableCollection or BindableCollection instead of list
When you assign the DataContext the second time, it doesn't technically change. This is because you are assigning it to the same collection. You should do something like this instead:
ObservableCollection<Class_ListViewItem> List_ListBoxItems = new ObservableCollection<Class_ListViewItem>();
public YourControl() {
InitializeComponent();
ListBox_BinsRegion.DataContext = List_ListBoxItems;
}
private void Button_Add_Click(object sender, RoutedEventArgs e)
{
Class_ListViewItem item = new Class_ListViewItem();
item.WH = this.comboBox_WareHouseBinsRegionDefinition.SelectedItem.ToString();
item.XXFrom = textBox_XXFrom.Text;
item.XXTo = textBox_XXTo.Text;
item.YYFrom = textBox_YYFrom.Text;
item.YYTo = textBox_YYTO.Text;
item.Z = textBox_ZFrom.Text;
List_ListBoxItems.Add(item);
}
Use an ObservableCollection<> rather than a List<>. This will update the binding automatically, with no need for the following line (which can be kind of slow)
ListBox_BinsRegion.DataContext = List_ListBoxItems;
You could either do what everyone else already suggested (using an ObservableCollection instead of the List) - or you could query the dependency property which is bound and find the corresponding Binding and refresh it manually.
I'd go for the ObservableCollection :)
Related
Overview:
I've set a binding on a ComoboBox to a List property. But when I run the application there is no data populated in the combo box.
Debug steps:
I checked the output window for binding errors which tells me that the data source might be null.
I then set a breakpoint on the setter of the QueryList property. This shows that the list count is 0. It seems the call to init executes after the setter is called on the property.
My thoughts are that the list is being initialized after the setter is called. Meaning that the binding will be null at that stage the binding is called on the combo box.
Question:
How can I call the Init method for my list prior to the QueryList setter being called?
Code snippet:
Code behind -
//The binding property for the combo box
private List<string> _queryList;
public List<string> QueryList
{
get
{
return this._queryList;
}
set
{
this._queryList = value;
}
}
public MainWindow()
{
InitializeComponent();
// Establish the Login control
Ctrl = new CrmLogin();
QueryList = new List<string>();
InitQueryList();
}
//Call to init the list data
private void InitQueryList()
{
_queryList.Add("Query queues with unapproved email routers");
_queryList.Add("Query queues with emails in pending send status");
}
Combobox binding setup -
<ComboBox HorizontalAlignment="Left" ItemsSource="{Binding QueryList}" Grid.Column="1" x:Name="queryComboBox" Grid.Row="0" VerticalAlignment="Bottom" Width="300" Visibility="Collapsed" Text="Select a query"/>
You forget to set your DataContext :
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
// Establish the Login control
Ctrl = new CrmLogin();
QueryList = new List<string>();
InitQueryList();
}
Try this :
public MainWindow()
{
// Establish the Login control
QueryList = new List<string>();
InitQueryList();
InitializeComponent();
Ctrl = new CrmLogin();
}
Firstly if you are using Code behind method MVC then you would need to update the datasource using
comboBox1.DataSource = QueryList;
Else if you are using the standard MVVM format then you would need to use
INotifyPropertyChanged
Else You will need to use
ObservableCollection
This happens because on initialization null value of your _querylist gets binded initially. Now when your querylist gets updated this doesnt get reflected in your view as View doesnt get any notification or event stating that a change has been made to the viewmodel(your binded item)
I have a User Control that contains a combobox/drop down list. This user control is used multiple times and added dynamically to a panel. When I change the value of the combobox on one user control, it changes the rest? Does anyone know how to sort this?
So to clarify. When I change the value in the combobox of the top usercontrol (7002), it will change the second user controls combobox value to whatever I selected.
Thanks!
Code for adding the controls;
foreach (Common.UserDTO UDTO in BLL.User.GetAllUsers())
{
Admin_UserControls.UserBar UB = new Admin_UserControls.UserBar(UDTO);
UB.Location = new Point(0, int.Equals(pnlUserBlock.Controls.Count, 0) ? 0 : pnlUserBlock.Controls[pnlUserBlock.Controls.Count - 1].Bottom);
pnlUserBlock.Controls.Add(UB);
}
constructor/load events:
private Common.UserDTO UDTO;
public UserBar(Common.UserDTO UDTO)
{
InitializeComponent();
/* Store the passed in UserDTO */
this.UDTO = UDTO;
}
private void UserBar_Load(object sender, EventArgs e)
{
/* Setup the Drop down list */
cbRanks.DataSource = Common.Helper.GetRanksDT();
cbRanks.DisplayMember = "Rank";
cbRanks.ValueMember = "ID";
/* Setup the users */
lblUsername.Text = UDTO.Username;
cbRanks.SelectedValue = UDTO.RankID;
}
Put the above comment into an answer should others have the same issue in future.
Each instance of the UserControl that is created is bound to the same DataSet giving you this result.
Caused by the line:
cbRanks.DataSource = Common.Helper.GetRanksDT();
To resolve this simply declare a new instance each time the UserControl is created, see this post that discusses a few methods.
I am using ComboBox in wpf as below and want to update ComboBox behind the seen if i update collection :-
<xmlns:dataProvider="clr-namespace:DataProvider"
<UserControl.Resources>
<dataProvider:BackOfficeDataProvider x:Key="DataProvider"/>
</UserControl.Resources>
<ComboBox x:Name="groupGroupNameCombo" HorizontalAlignment="Left" Margin="368,123,0,0" VerticalAlignment="Top" Width="226" Height="31" SelectionChanged="groupGroupNameCombo_SelectionChanged" DisplayMemberPath="GroupName" SelectedItem="{Binding ParentID, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding GroupParentList, Mode=TwoWay, NotifyOnTargetUpdated=True, NotifyOnSourceUpdated=True, Source={StaticResource DataProvider}}" IsSynchronizedWithCurrentItem="True">
</ComboBox>
Class BackOfficeDataProvider {
public static ObservableCollection<Categories> groupParentList = null;
public virtual ObservableCollection<Categories> GroupParentList
{
get { return groupParentList ; }
set
{
groupParentList = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("GroupParentList");
}
}
public void loadComboListData();
{
GroupParentList = (ObservableCollection<Categories>) //fetching data from database using NHibernate directly getting list ;
}
}
my front end class which has refresh button :-
private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
new BackOfficeDataProvider().loadComboListData();
}
when application load that time i can see the item in combobox but when i click on Refresh button that time it load updated data from database but not updating combobox untill i use below code
groupGroupNameCombo.ItemsSource = null;
groupGroupNameCombo.ItemSource = GroupParentList ;
Its a manually thing i have to do always to refresh combobox, how can i make it automatic like if i update collection then it should update combobox at the same time and i don't need to use above workaround.
I think this may have something to do with breaking the coupling between the combobox and the ObservableCollection when doing this:
GroupParentList = //fetching data from database;
Try this instead:
var dbCategories = // Get data from DB
GroupParentList.Clear();
foreach (var item in dbData)
GroupParentList.Add(item);
The point is to update the items in the collection, not the collection itself.
Also, try defining your collection like this, it should'nt have to be instantiated more than once (i.e no setter):
public static ObservableCollection<Categories> groupParentList = null;
public virtual ObservableCollection<Categories> GroupParentList
{
get
{
if (groupParentList == null)
groupParentList = new ObservableCollection<Categories>();
return groupParentList;
}
}
Hogler is right, your approach of assigning a new ObservableCollection object to the binding property will break how binding works. For ObservableCollection to work, you will need to modify the items in the collection itself, ObservableCollection is responsible of publishing list changes to the binding target. When you assign a new collection to the binding target, the list will not get refresh unless you published PropertyChanged event again to register this new binding source.
In your later comment you did state that you only instantiate ObservableCollection once only, which is not obvious from your posted code. It appears to me that the reason why it doesn't work is because you assign a new collection to the "GroupParentList" each time you run "loadComboListData".
Try this ..
once You are done getting data from your database in groupParentList , Add below Line, it will work as below :-
GroupParentList = new ObservableCollection<Categories>(groupParentList )
I have a datagrid in my wpf application and I have a simple problem. I have a generic list and I want to bind this collection to my datagrid data source every time an object is being added to the collection. and I'm not interested to use observable collection.
the point is I'm using the same method somewhere else and that works fine. but this time when i press Add button an object is added and datagrid updates correctly but from the second item added to collection datagrid does not update anymore.
Here is the Code :
private void btnAddItem_Click(object sender, RoutedEventArgs e)
{
OrderDetailObjects.Add(new OrderDetailObject
{
Price = currentitem.Price.Value,
Quantity = int.Parse(txtQuantity.Text),
Title = currentitem.DisplayName,
TotalPrice = currentitem.Price.Value * int.Parse(txtQuantity.Text)
});
dgOrderDetail.ItemsSource = OrderDetailObjects;
dgOrderDetail.UpdateLayout();
}
any idea ?
The ItemsSource is always the same, a reference to your collection, no change, no update. You could null it out before:
dgOrderDetail.ItemsSource = null;
dgOrderDetail.ItemsSource = OrderDetailObjects;
Alternatively you could also just refresh the Items:
dgOrderDetail.ItemsSource = OrderDetailObjects; //Preferably do this somewhere else, not in the add method.
dgOrderDetail.Items.Refresh();
I do not think you actually want to call UpdateLayout there...
(Refusing to use an ObservableCollection is not quite a good idea)
I also found that just doing
dgOrderDetails.Items.Refresh();
would also accomplish the same behavior.
If you bind the ItemSource to a filtered list with for example Lambda its not updated.
Use ICollectionView to solve this problem (Comment dont work):
//WindowMain.tvTemplateSolutions.ItemsSource = this.Context.Solutions.Local.Where(obj=>obj.IsTemplate); // templates
ICollectionView viewTemplateSolution = CollectionViewSource.GetDefaultView(this.Context.Solutions.Local);
viewTemplateSolution.SortDescriptions.Clear();
viewTemplateSolution.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
viewTemplateSolution.Filter = obj =>
{
Solution solution = (Solution) obj;
return solution.IsTemplate;
};
WindowMain.tvTemplateSolutions.ItemsSource = viewTemplateSolution;
i use ObservableCollection as my items collection and than in the view model
call CollectionViewSource.GetDefaultView(my_collection).Refresh();
I have a listbox on my WinForms where users can move the items up and down and that listbox is as well the same as a list I have and I was wondering what would be the most efficient way to maintain both synchronized.
for example to move an item down I have:
int i = this.recoveryList.SelectedIndex;
object o = this.recoveryList.SelectedItem;
if (i < recoveryList.Items.Count - 1)
{
this.recoveryList.Items.RemoveAt(i);
this.recoveryList.Items.Insert(i + 1, o);
this.recoveryList.SelectedIndex = i + 1;
}
And I have:
public List<RouteList> Recovery = new List<RouteList>();
Which I would like to maintain updated against the listbox.
Should I simple clear Recovery and update with the current listbox data or is there a better way to update both when move up and down ?
I am mainly asking because the types from the listbox to the list are different.
.Net provides built-in support for this type of behavior. In order to use it, you need to change the type of your Recovery list to:
public BindingList<RouteList> Recovery = new BindingList<RouteList>();
And then you use that BindingList as the DataSource in your controls:
listBox1.DataSource = Recovery;
Here's a simple example using a BindingList of String. I have two listBox's on the form, and they both stay in sync as the selected element gets swapped with the first element in the list:
public partial class Form1 : Form
{
private readonly BindingList<string> list = new BindingList<string> { "apple", "pear", "grape", "taco", "screwdriver" };
public Form1()
{
InitializeComponent();
listBox1.DataSource = list;
listBox2.DataSource = list;
}
private void listBox1_KeyUp(object sender, KeyEventArgs e)
{
var tmp = list[0];
list[0] = list[listBox1.SelectedIndex];
list[listBox1.SelectedIndex] = tmp;
}
}
The proper way is to change the underlying object and then have the UI Control react to that change.
For the ListBox to react to changes in your object collection (your List) you'd need to use an ObservableCollection instead. It's like the INotifyPropertyChanged for collections.
Then you make your up/down actions change the collection, NOT the UI.
EDIT
I am not saying to add an observer on TOP of the collection. I'm saying to change the type of your collection. Don't use List, use ObservableCollection. It works (largely) the same way but notifies the bound UI Controls of changes to it's items.
As for an example, please Google for it. That's what i'd have to do to provide one anyway..