DataGrid NewItemPlaceholderPosition lost when ItemsSource changed - c#

I have two DataGrids above one another, and selecting a row in the top grid, OrdersGrid, displays some details in the bottom grid, DetailsGrid, about the selected row from OrdersGrid.
I would like the NewItemPlaceholderPosition to be AtBeginning for both grids. This is easy enough for OrdersGrid because I can just set it in my UserControl subclass constructor:
((IEditableCollectionView)OrdersGrid.Items).NewItemPlaceholderPosition = NewItemPlaceholderPosition.AtBeginning;
But the problem is that the ItemsSource for DetailsGrid is a member of the currently selected item in OrdersGrid.
If I set NewItemPlaceholderPosition for DetailsGrid as above then it works until I click on a new row in OrdersGrid when it goes back to the default of being at the bottom since DetailsGrid reloads from its new ItemsSource.
The ItemsSource for OrdersGrid is an ObservableCollection called Orders, which contains Objects of type Order, and the ItemsSource for DetailsGrid is Order.Details, also an ObservableCollection, for the current Order.
I'm thinking I want something like an ItemsSourceChanged event for DetailsGrid, but I'm not sure if this is the correct approach or even how to go about this. Please help!

I was able to solve this problem by adding the following to my UserControl subclass constructor:
((IEditableCollectionView)DetailsGrid.Items).NewItemPlaceholderPosition = NewItemPlaceholderPosition.AtBeginning;
var dpd = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(DataGrid));
if (dpd != null)
{
dpd.AddValueChanged(DetailsGrid, DetailsSourceChanged);
}
with the DetailsSourceChanged method defined as followed:
private void DetailsSourceChanged(object sender, EventArgs e)
{
if (DetailsGrid.Items.Count > 0)
{
((IEditableCollectionView)DetailsGrid.Items).NewItemPlaceholderPosition = NewItemPlaceholderPosition.AtBeginning;
}
}

Related

C# WinForms ListBox changes selection to first item

I got a problem with a ListBox in a WinForm application. I have two ListBoxes inside of a tab control and depending on the selection in the first one (lb1), the DataSource of the second one (lb2) changes. This is done in the SelectedValueChanged Event.
private void listBox_ControlUnits_SelectedValueChanged(object sender, EventArgs e)
{
ControlUnit unit = (sender as ListBox).SelectedItem as ControlUnit;
textBox_ProjectNameTab.Text = unit.ProjectName;
listBox_ControlCircuits.DataSource = null;
listBox_ControlCircuits.DataSource = unit.ControlCircuits;
}
lb1 is filled with a DataSource, too.
Now if I select a value in lb1 the selection automatically jumps back to the first item and I can not figure out why. is this some kind of UI update problem?
Even without the SelectedValueChanged event and the connection to the second listbox the issue occures.
Short gif of the problem, sorry for the blurriness
If I select one item more than once it works somehow (as seen in the gif).
Edit:
I found the problem but I do not quite understand what happens.
I have another listBox on another tab of my tab control. This listBox has the same DataSource as lb1. This seems to cause this behavior.
I finally found the problem:
I did not know that if I use the same DataSource for two ListBoxes they share the BindingContext per default.
I created a new BindingContext for the second ListBox and now the selection does no longer change.
listBox_allGroups.DataSource = null;
listBox_allGroups.DataSource = x.y;
listBox_allGroups.DisplayMember = "Name";
listBox_ControlUnits.DataSource = null;
listBox_ControlUnits.DataSource = x.y;
listBox_ControlUnits.DisplayMember = "Name";
listBox_ControlUnits.BindingContext = new BindingContext();
You can use a variable to hold the selected item
object _selecteditem=null;
and check it in ListBox click event.
prive void ListBox1_Click(object sender,EventArgs e)
{
if(ListBox1.SelectItem == _selecteditem) return;
// do ...
}

How to Add new Item to XAML Datagrid using Keyboard Tab press when last item is selected?

I have a pretty basic Datagrid XAML bound to a CollectionViewSource.
<DataGrid ItemsSource="{Binding Source={StaticResource EditingItemsCollectionViewSource}}"/>
And the Collection View Source is bound to an observable collection of very basic items with 3 numerical values. C# obviously.
I want to be able to add a new row (add a new item) at the bottom of this datagrid by pressing Tab on the keyboard when I am in the last cell of the last row.
Is this possible?
One possible solution is to programmatically set the property:
dataGrid.AllowUserToAddRows = true;
in order to implement "Add Row" functionality (provided that it was originally set to false, thus the new row was invisible). As per your task definition, it could be triggered by Tab key press (with any additional condition you may add):
private void dataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
try
{
if (e.Key == Key.Tab)
{
e.Handled = true;
// your code
}
}
catch{}
}
You may also want to set some default values for newly created row item by adding event handling procedure:
dataGrid.InitializingNewItem += new InitializingNewItemEventHandler(dataGrid_InitNewItem);
private void dataGrid_InitNewItem(object sender, InitializingNewItemEventArgs e)
{
// your code
}
Other sample implementations of adding row to WPF DataGrid could be found here: Wpf DataGrid Add new row
Also, pertinent to your description, you can add the item to the underlying ObservableCollection, so it will automatically appear in the DataGrid.
Hope this will help. Best regards,

Databinding combobox to empty List<CustomObject>

I have a form which has two ComboBoxes. I'd like to use the second (child) ComboBox to display a list of child objects based on user selection of an item from the first.
When the form is instantiated, I databind both controls to private List<Widget>s like so:
private List<ParentWidget> _parentList;
private List<ChildWidget> _childList;
public FormExample()
{
InitializeComponent();
_parentList = GetParentWidgets();
_childList = new List<ChildWidget>();
cmbParent.DisplayMember = "WidgetName";
cmbParent.ValueMember = "ID";
cmbParent.DataSource = _parentList;
cmbChild.DisplayMember = "WidgetName";
cmbChild.ValueMember = "ID";
cmbChild.DataSource = _childList;
}
When the parent selected index is changed, I then populate _childList with the appropriate objects.
The problem is the child ComboBox never shows any of the objects in the collection. It works if I populate the collection with at least one ChildWidget before databinding it, but I'd like it to start empty.
If I understand correctly from another answer, this is failing because the empty list does not contain any properties to bind to. However I am binding to a specific class (Widget) rather than a generic object. Is this not sufficient for the databinding?
When using binding, you should better use the BindingList<> instead, your problem is the List<> does not support notifying changes, so when the data is changed, the control does not know about it and update accordingly. You can use the BindingList<> instead like this:
private BindingList<ParentWidget> _parentList;
private BindingList<ChildWidget> _childList;
That means you have to change the return type of the method GetParentWidgets() to BindingList<ParentWidget>, or you can also use the constructor of a BindingList<> like this:
_parentList = new BindingList<ParentWidget>(GetParentWidgets());
What I would do is listening to the valueChanged event of the parent comboBox and when the event fire I would do the databinding on the child combo.
cmbParent.SelectedValueChanged += OnParentSelectedValueChanged;
private void OnParentSelectedValueChanged(object sender, EventArgs e)
{
this.UpdateChildList(); // Update the data depending on the value in the parent combo
cmbChild.DisplayMember = "WidgetName"; // I guess you can still do this in the constructor
cmbChild.ValueMember = "ID"; // I guess you can still do this in the constructor
cmbChild.DataSource = _childList;
}

edit item' is not allowed for this view within datagrid wpf C#

I am using WPF DataGrid and I'm unable to edit data in it. When I double click a cell I get the error listed in the discussion name:
'Edit item' is not allowed for this view.
How can I overcome this ?
Thanks
Edit:
There are two data in my students' name and what they want to say, both of string. Then, I'm binding with my entityframework.
Below is my selectionchanged function
private void ClassDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectedStudent = (Class)e.AddedItems[0];
var sayingCollection = selectedStudent.;
sayingCollection.AssociationChanged += ColumnsCollection_Changed;
var sayingViewSource = ((CollectionViewSource)(this.FindResource("StudentsSayingViewSource")));
sayingViewSource.Source = sayingCollection.Where(c => c.Saying.Contains(":"));
}
Let You confirm us whether you bound a Collection Item such as either IList , ObservableCollection or IEnumerable in the ItemSource.
Because the IEnumerable object doesn't have an add\remove property. So You couldn't modify the collection.

Listbox items at the bottom were not automatically selected

I have this subtle program regarding the behavior of listbox. My listbox is binded with an observable list in the viewmodel. There are 2 ways in addding an item in the listbox. First is ADD a single item then that item would be selected directly. This works fine.
The second way was LOAD which by its name will be adding more than 1 item in the lisbox. Now the problem is when loading items more than the listbox can accomodate in the view, those items that are not in view (items at the bottom thus need to be scrolled in order for it to be viewed) was not automatically selected...
Only the items that are by default viewed are the ones selected:
private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
if (listBoxAddresses.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) return;
for (int i = 0; i < TestSetting.DeviceSettings.Count; i++)
{
ListBoxItem myListBoxItem = (ListBoxItem)(listBoxAddresses.ItemContainerGenerator.ContainerFromItem(TestSetting.DeviceSettings[i]));
if (myListBoxItem != null)
{
myListBoxItem.IsSelected = true;
}
}
listBoxAddresses.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged;
}
I wonder if this is just a natural behavior for listbox.
I just realize this now...setting my listbox to :
VirtualizingStackPanel.IsVirtualizing="False"
did all the trick. Thanks to Dr.WPF for the idea. Though there are consequences for turning off virtualization (performance) but it won't matter that much.

Categories