I am working on WPF and I am using a ListView, and I need to fire an event when an item is added to it. I have tried this:
var dependencyPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ListView));
if (dependencyPropertyDescriptor != null)
{
dependencyPropertyDescriptor.AddValueChanged(this, ItemsSourcePropertyChangedCallback);
}
.....
private void ItemsSourcePropertyChangedCallback(object sender, EventArgs e)
{
RaiseItemsSourcePropertyChangedEvent();
}
But It seems to be working only when the entire collection is changed, I have read this post: event-fired-when-item-is-added-to-listview, but the best answer applies for a listBox only. I tried to change the code to ListView but I wasnt able to do that.
I hope You can help me. Thank you in advance.
Note this only works for a WPF Listview!
After some research I have found the answer to my question and It's really easy:
public MyControl()
{
InitializeComponent();
((INotifyCollectionChanged)listView.Items).CollectionChanged += ListView_CollectionChanged;
}
private void ListView_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
// scroll the new item into view
listView.ScrollIntoView(e.NewItems[0]);
}
}
Actually, the NotifyCollectionChangedAction enum allows your program to inform you about any change such as: Add, Move, Replace, Remove and Reset.
Note: This solution was meant for a WinForms ListView.
In my case I ended up coming to a fork in the road with 2 choices...
(1) Create a custom ListView control that inherits a ListView's class. Then add a new event to be raised when any item is added, deleted, or ListView is cleared. This path seemed really messy and long. Not to mention the other big issue that I would need to replace all my original ListViews with the newly created Custom ListView control. So I passed on this!
(2) With every add, delete, or clear call to the listview I also called another function simulating the CollectionChanged event.
Create the new event like function...
private void myListViewControl_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
//The projects ListView has been changed
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
MessageBox.Show("An Item Has Been Added To The ListView!");
break;
case NotifyCollectionChangedAction.Reset:
MessageBox.Show("The ListView Has Been Cleared!");
break;
}
}
Add an item to the ListView elsewhere...
ListViewItem lvi = new ListViewItem("ListViewItem 1");
lvi.SubItems.Add("My Subitem 1");
myListViewControl.Items.Add(lvi);
myListViewControl_CollectionChanged(myListViewControl, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, lvi, lvi.Index));
Clear the ListView elsewhere...
myListViewControl.Items.Clear();
myListViewControl_CollectionChanged(myListViewControl, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Related
I am a student learning xamarin forms, I am trying to create a basic chat app in this I want to know how to get position of current item in listview that's user watching. When a new message received i want to know if user is at bottom or not if at bottom focus the new and if not at the bottom then just add not by adding focus to it.
you get the selected item from the Xamarin.Forms.ListView.SelectedItem property of your ListView.
If your ListView.ItemSource is of a type that allows using IndexOf you can now do something like
int position = (yourlistview.ItemSource as ObservableCollection<your type>).IndexOf(yourlistview.SelectedItem)
Update:
ok I think i understood what you want.
In most cases more than one item is currently shown when using a listview. So their exists not a single index
but i think you just want to know if the last item of the list is visible/the user has scrolled to the end?
If so ListView has an ItemAppearing event. I use it for example to load more data from an websource if the user scrolled through the first 100 items.
You could do something like this
listview.ItemAppearing += listviewItemAppearing;
listview.ItemDisappearing += listviewItemDisappearing;
bool m_scrolledToEnd;
private void listviewItemDisappearing(object sender, ItemVisibilityEventArgs e)
{
if(e.Item == yourlastiem)
m_scrolledToEnd = false;
}
private void listviewItemAppearing(object sender, ItemVisibilityEventArgs e)
{
if(e.Item == yourlastiem)
m_scrolledToEnd = true;
}
if you realy need to know if a specific index is shown you could create a List<int> m_idxlist;
and in the appearing event add the index of the item to the list
and in the disappearing event remove the index of the item from the list.
Then you will have a list where all indexes of the items currently shown are stored.
From the Documentation
ListView supports selection of one item at a time. Selection is on by
default. When a user taps an item, two events are fired: ItemTapped
and ItemSelected. Note that tapping the same item twice will not fire
multiple ItemSelected events, but will fire multiple ItemTapped
events. Also note that ItemSelected will be called if an item is
deselected.
To detect selecting an item, you can add a method, onSelection:
void OnSelection (object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem == null) {
return; //ItemSelected is called on deselection, which results in SelectedItem being set to null
}
DisplayAlert ("Item Selected", e.SelectedItem.ToString (), "Ok");
//((ListView)sender).SelectedItem = null; //uncomment line if you want to disable the visual selection state.
}
To disable selection just set the selectedItem to null:
SelectionDemoList.ItemSelected += (sender, e) => {
((ListView)sender).SelectedItem = null;
};
I have a piece of code that will add the selected item of a combobox to a listbox when a checkbox is checked. I would like to remove that selected item to be removed from the listbox when the checkbox is unchecked.
My problem is I cant simply repeat the code for removal to be the same as add because the combobox selection will different or empty when unchecked.
This is how it currently looks:
private void CBwasher_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked == true)
{
listBox2.Items.Add(comboBox1.SelectedItem);
}
if (checkBox1.Checked == false)
{
listBox2.Items.Remove(comboBox1.SelectedItem);
}
So I need a way of removing whatever was added by that check change instead of removing the selected index of the combobox. Please consider that there may be multiple lines in the listbox added by multiple different checkboxes.
You simply need to store the added item and remove it.
private object addedItem;
private void CBwasher_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
addedItem = comboBox1.SelectedItem;
listBox2.Items.Add(addedItem);
}
else
{
listBox2.Items.Remove(addedItem);
}
}
You may also need to check SelectedItem is null before adding/removing the item.
Focusing on the part where you said there might be multiple different checkboxes,
you need to store one item per checkbox.
You can write your own child class of the checbox control to add this feature, or simply use the Tag property.
You can also indicate which checkbox is linked to which combobox in the same way. Either child class or use the Tag property.
In my example, I'll assume you've referenced the combobox from the checkbox using the Tag property.
You can do it manually like this
checkBox1.Tag = comboBox1;
or hopefully you can automate it if you are generating these on the fly.
Here is the general idea of how the checkbox event should look.
The event is is utilising the sender argument, which means you should hook up all your checkboxes CheckedChanged events to this one handler. No need to create separate handlers for each.
private void CBwasher_CheckedChanged(object sender, EventArgs e)
{
var checkBox = (CheckBox)sender;
var comboBox = (ComboBox)checkBox.Tag;
if (checkBox.Checked && comboBox.SelectedItem != null)
{
listBox2.Items.Add(comboBox.SelectedItem);
comboBox.Tag = comboBox.SelectedItem;
}
if (!checkBox.Checked && comboBox.Tag != null)
{
listBox2.Items.Remove(comboBox.Tag);
}
}
Okay, so I have four listBox controls. I want to select the same index on all four listBox when one item is clicked on any of them. To be mentioned, I do change the index sometimes in the program. I tryed using a method listSelectChange (int index) and adding for each listBox an event for selectIndexChange, but it would activate the event even if the select is made by the program and not by user-control.
Please don't use classes, just a brute method would be fine!
You can unsubscribe from selectedIndexChanged before you update the ListBox and re-subscribe to it immediately after that. It's a common practice.
Since you gave no code example I'm doing some guessing here.
// Enumerable of all the synchronized list boxes
IEnumerable<ListBox> mListBoxes = ...
...
public void OnSelectedIndexChanged(object sender, EventArgs e) {
var currentListBox = (ListBox)sender;
// Do this for every listbox that isn't the one that was just updated
foreach(var listBox in mListBoxes.Where(lb => lb != currentListBox)) {
listBox.SelectedIndexChanged -= OnSelectedIndexChanged;
listBox.SelectedIndex = currentListBox.SelectedIndex;
listBox.SelectedIndexChanged += OnSelectedIndexChanged;
}
}
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,
Thanks to Peter Duniho's tip on how a Stack Overflow question has to look like, I converted my original question and its title hopefully to something more appropriate.
So I'm currently working on a conversation editor for a chatbot which consists of a treeview and two listviews. I store each treenode as a key(int) in a dictionary. The dictionary structure looks like this:
Dictionary<selectedNode(int), Tuple<List<listView1ItemLabels(string)>, List<listView2ItemLabels(string)>>>
Each list in the Tuple holds the labels of the items which are dynamically added to the two listviews at runtime using this custom function:
void AddItemToListView1(string itemName)
{
// add new Item to listView1 and
dictionary[selectedNodeID].Item1.Add(itemName);
// add its Text to the dictionary Tuple first generic list
listView1.Items.Add(itemName);
}
When I click a node (or rather "the corresponding dictionary key"), both listviews will be repopulated from the lists in the Tuple via the AfterSelect event of the treeView, which looks like this:
private void treeView_AfterSelect(object sender, TreeViewEventArgs e)
{
foreach(string str in dictionary[selectedNodeID].Item1)
{
listView1.Items.Add(str);
}
foreach(string str in dictionary[selectedNodeID].Item2)
{
listView2.Items.Add(str);
}
}
What I want to achieve is to change the string in the tuple's lists according to the change that happens to the listview item inside the AfterLabelEdit event. My approach below is obviously incorrect, even though the methods are valid:
private void listView1_BeforeLabelEdit(object sender, LabelEditEventArgs e)
{
// Capture the yet unedited label of the item
oldLabel = e.Label;
}
private void listView1_AfterLabelEdit(object sender, LabelEditEventArgs e)
{
if(e.Label == null)
return;
if(e.Label == "")
e.CancelEdit = true;
// remove the selected listview item label, which was previously added using the custom AddItem() function
dictionary[selectedNodeID].Item1.Add(e.Label);
dictionary[selectedNodeID].Item1.Remove(oldLabel);
}
I really don't see any reason why this won't work. What am I missing?
EDIT: Here's a picture of the GUI. May be it helps people understand what this is all about. :-)