My problem is about handling drag and drop in a ListView.
So I get the selected ListViewItem.
ListView.SelectedListViewItemCollection itemCollection = (ListView.SelectedListViewItemCollection)e.Data.GetData("System.Windows.Forms.ListView+SelectedListViewItemCollection");
If i move a new element via drag&drop (for example from windows explorer), the itemCollection equals null, because i dont select an item in the listview.
private void DragDropHandler(object sender, DragEventArgs e)
{
ListView.SelectedListViewItemCollection itemCollection = (ListView.SelectedListViewItemCollection)e.Data.GetData("System.Windows.Forms.ListView+SelectedListViewItemCollection");
if (itemCollection == null)
{
itemCollection = (ListView.SelectedListViewItemCollection)e.Data.GetData("System.Windows.Forms.ListView");
}
}
for this case I would get the last element in list view, how can i do that?
Try this:
var r = Enumerable.Empty<ListViewItem>();
if(this.listView1.Items.Count > 0)
r = this.listView1.Items.OfType<ListViewItem>();
var last = r.LastOrDefault();
if (last != null)
{
// do your stuff
}
Related
I am working on a UWP application trying to implement the Transform3DAnimations as from the platform samples using a GridView instead of a HubSection. I've got it all figured out except for fetching the GridViewItem. The below code is from the sample in context to a hubsection.
private void UpdateRandomSection()
{
// Updating the section triggers a cool animation!
// See SectionView.xaml and SectionView.xaml.cs
var sectionsInView = HeadlinesHub.SectionsInView;
var sectionsCount = sectionsInView.Count;
if (sectionsCount > 0)
{
var sectionToUpdate = sectionsInView[_random.Next(sectionsCount)];
sectionToUpdate.DataContext = new HeroArticlesViewModel();
}
}
I am trying to fetch the GridViewItem, but I am unable to fetch the GridViewItem it always returns me the data model of the GridViewItem. How can I get the GridViewItem from the GridView? My code is below:
private Random InAppLiveTileRandomTileNumberGenerator;
private void UpdateRandomSection()
{
var sectionsInView = AllDevicesGridView.Items;
var sectionsCount = sectionsInView.Count;
if (sectionsCount > 0)
{
var sectionToUpdate = (GridViewItem)AllDevicesGridView.Items[InAppLiveTileRandomTileNumberGenerator.Next(sectionsCount)]; //Invalid Cast exception here
sectionToUpdate.DataContext = new InappLiveTileViewModelModel();
}
}
Link to the sample
Solution I tried from stack answers
For your code line var sectionToUpdate = (GridViewItem)AllDevicesGridView.Items[InAppLiveTileRandomTileNumberGenerator.Next(sectionsCount)]; it will returns the data model of the GridViewItem as you seen. To get the GridViewItem you may need to use ContainerFromItem method. For example, if you want to get the GridViewItem object from the selected item code may as follows:
private void listViewDirectory_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var container = AllDevicesGridView.ContainerFromItem(AllDevicesGridView.SelectedItem);
GridViewItem item = container as GridViewItem;
System.Diagnostics.Debug.WriteLine(item.ActualHeight);
}
It seems like you want get a special item with item index assigned:
var container = AllDevicesGridView.ContainerFromItem(AllDevicesGridView.Items[1]);
GridViewItem item = container as GridViewItem;
If you want to get data context of the GridViewItem, you may need to get the ListViewItemPresenter from GridViewItem. Here I use the VisualTreeHelper to get the ListViewItemPresenter. By the way, VisualTreeHelper is not recommended to use unless you don't have other ways.
private void listViewDirectory_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var container = AllDevicesGridView.ContainerFromItem(AllDevicesGridView.Items[1]);
GridViewItem item = container as GridViewItem;
ListViewItemPresenter itempresenter;
IEnumerable<ListViewItemPresenter> items = FindVisualChildren<ListViewItemPresenter>(item);
int count = items.Count();
itempresenter = items.ElementAt<ListViewItemPresenter>(0);
itempresenter.DataContext = new Person() { Name = "update", Contact = "update" };
}
private static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
I am really confused on thinking which control to use for my purpose.
I am having list of items say item1 to item10. User can select 4 or 5 items in any order.
Now user selected items has to be separated in same order.
For example, if the user selected the items in following order, item4, item8, item3 and item2.
I want it in the same order. item4,item8,item3,item2.
How do I achieve this in winforms control?
It is not a very nice solution but I wrote it as you asked.
Set the SelectionMode of your ListBox to MultiExtended or MultiSimple as you need.
Then write this code in SelectedIndexChanged event of your ListBox:
List<string> orderedSelection = new List<string>();
bool flag = true;
private void listBox3_SelectedIndexChanged(object sender, EventArgs e)
{
if (flag)
{
flag = false;
var list1 = listBox3.SelectedItems.Cast<string>().ToList();
if (listBox3.SelectedItems.Count > orderedSelection.Count)
{
orderedSelection.Add(list1.Except(orderedSelection).First());
}
else if (listBox3.SelectedItems.Count < orderedSelection.Count)
{
orderedSelection.Remove(orderedSelection.Except(list1).First());
}
var list2 = listBox3.Items.Cast<string>().Except(list1).ToList();
listBox3.Items.Clear();
for (int i = 0; i < list1.Count; i++)
{
listBox3.Items.Add(list1[i]);
listBox3.SelectedIndex = i;
}
foreach (string s in list2)
{
listBox3.Items.Add(s);
}
flag = true;
}
}
When user selects an item, It comes to first of the list and the rest of the items comes next.
Also, there is an alternative way. You can use a CheckedListBox with two extra button for moving selected items up and down. So user can change the order of selected items.
This solution uses CheckListBox's ItemCheck event along with a private List to keep track of the click items order.
protected List<string> clickOrderList = new List<string>();
private void Form1_Load(object sender, EventArgs e)
{
// Populate the checked ListBox
this.checkedListBox1.Items.Add("Row1");
this.checkedListBox1.Items.Add("Row2");
this.checkedListBox1.Items.Add("Row3");
this.checkedListBox1.Items.Add("Row4");
}
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (sender != null && e != null)
{
// Get the checkListBox selected time and it's CheckState
CheckedListBox checkListBox = (CheckedListBox)sender;
string selectedItem = checkListBox.SelectedItem.ToString();
// If curent value was checked, then remove from list
if (e.CurrentValue == CheckState.Checked &&
clickOrderList.Contains(selectedItem))
{
clickOrderList.Remove(selectedItem);
}
// else if new value is checked, then add to list
else if (e.NewValue == CheckState.Checked &&
!clickOrderList.Contains(selectedItem))
{
clickOrderList.Insert(0, selectedItem);
}
}
}
private void ShowClickOrderButton_Click(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
foreach (string s in clickOrderList)
{
sb.AppendLine(s);
}
MessageBox.Show(sb.ToString());
}
I have a listbox , I add items dynamically to the listbox.
I want the listbox to autoscroll to the last item added.
I used
List<string> ItemsList = new List<string>
public void InsertItem(string newItem)
{
ItemsList.Add(status);
if (ItemsList.Count > MaxSize)
{
ItemsList.RemoveAt(0);
}
lb.Items.Refresh();
lb.SelectedIndex = lb.Items.Count - 1;
lb.ScrollIntoView(status);
}
but this works only before my application is initialized (i.e I added some items before the application starts)
But after the application has started if I try adding items,the scroll bar is not auto scrolling to the last added item
Could any one tell solution for this
This has turned out to be quite a mission actually, because the ScrollIntoView only works the first time it is called. Every other call after that will not work for some reason.
The way around this, would be to find the "ScrollInfo" of the listbox and set the scroll value. See example below
public static void AutoScrollToCurrentItem(ListBox listBox, int index)
{
// Find a container
UIElement container = null;
for (int i = index; i > 0; i--)
{
container = listBox.ItemContainerGenerator.ContainerFromIndex(i) as UIElement;
if (container != null)
{
break;
}
}
if (container == null)
return;
// Find the ScrollContentPresenter
ScrollContentPresenter presenter = null;
for (Visual vis = container; vis != null && vis != listBox; vis = VisualTreeHelper.GetParent(vis) as Visual)
if ((presenter = vis as ScrollContentPresenter) != null)
break;
if (presenter == null)
return;
// Find the IScrollInfo
var scrollInfo =
!presenter.CanContentScroll ? presenter :
presenter.Content as IScrollInfo ??
FirstVisualChild(presenter.Content as ItemsPresenter) as IScrollInfo ??
presenter;
// Find the amount of items that is "Visible" in the ListBox
var height = (container as ListBoxItem).ActualHeight;
var lbHeight = listBox.ActualHeight;
var showCount = (int)(lbHeight / height) - 1;
//Set the scrollbar
if (scrollInfo.CanVerticallyScroll)
scrollInfo.SetVerticalOffset(index - showCount);
}
private static DependencyObject FirstVisualChild(Visual visual)
{
if (visual == null) return null;
if (VisualTreeHelper.GetChildrenCount(visual) == 0) return null;
return VisualTreeHelper.GetChild(visual, 0);
}
The way to use the above code can be like this:
private void Button_Click(object sender, RoutedEventArgs e)
{
listBox1.Items.Add("New Item");
AutoScrollToCurrentItem(listBox1, listBox1.Items.Count);
}
I really wish I had enough points to comment on your question. How is your ItemsList connected to your ListBox? After you start your application, can you manually scroll down to the new items?
Most likely you binded ItemsList to ListBox. If so, I suggest you change List<string> to ObservableCollection<string> so that ListBox can automatically update itself.
ObservableCollection
if u can update listbox automatically then use ObservableCollection.
private ObservableCollection<string> _source = new ObservableCollection<string>();
and your insert method:
private void Insert(string item)
{
_source.Add(item);
lb.SelectedIndex = _source.Count - 1;
lb.ScrollIntoView(ListBox.SelectedItem);
}
I have search page which may return multiple search results in separate DataGrid controls, or if the search is specific enough, a single result in a single grid.
Should only one result be found, I then want to invoke the click of a ButtonColumn in the sole row of that grid to then open a separate page, as if the user clicked it themselves.
Here is my Page_LoadComplete event handler:
protected void Page_LoadComplete(object sender, EventArgs e)
{
var allControls = new List<DataGrid>();
// Grab a list of all DataGrid controls on the page.
GetControlList(Page.Controls, allControls);
var itemsFound = allControls.Sum(childControl => childControl.Items.Count);
for (var i = 0; i <= allControls.Count; i++)
{
itemsFound += allControls[i].Items.Count;
// If we're at the end of the for loop and only one row has
// been found, I want to get a reference to the ButtonColumn.
if (i == allControls.Count && itemsFound == 1)
{
var singletonDataGrid = allControls[i];
// **Here** I want to reference the ButtonColumn and then
// programmatically click it??
}
}
}
How can I obtain a reference to the ButtonColumn in question and then proceed to programmatically click it?
Found a solution. I programmatically invoke the OnSelectedIndexChanged event handler (defined as Select_Change) like so:
protected void Page_LoadComplete(object sender, EventArgs e)
{
var allControls = new List<DataGrid>();
// Grab a list of all DataGrid controls.
GetControlList(Page.Controls, allControls);
var itemsFound =
allControls.Sum(childControl => childControl.Items.Count);
for (var i = 0; i < allControls.Count; i++)
{
if (allControls.Count > 0 && allControls[i].ID == "grid")
{
// If a single row is found, grab a reference to the
// ButtonColumn in the associated grid.
if (i == (allControls.Count - 1) && itemsFound == 1)
{
var singletonDataGrid = allControls[i];
singletonDataGrid.SelectedIndex = 0;
Select_Change(singletonDataGrid, new EventArgs());
}
}
}
}
Using the reference to the DataGrid with the single row, I also set its SelectedIndex to the first (and only) row so that I can proceed with other operations after the invocation begins.
Is there some event I can use to tell when the SelectedIndices property changes for a listbox? I want to deselect items in a listbox based on a certain property value of the item. I've hooked up an event that works for when a SelectedIndex is changed, but not sure how to do it for when the SelectedIndices property changes for multiselection.
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Curve curItem = (Curve)listBox1.SelectedItem;
int index = listBox1.Items.IndexOf(curItem);
if (curItem.newName == null)
{
listBox1.SetSelected(index, false);
}
}
You could use ListBox.SelectedItems and LINQ to find all Curves with newName==null to deselect them:
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
var nullNameCurves = listBox1.SelectedItems
.Cast<Curve>()
.Where(c => c.newName == null)
.ToList();
listBox1.SelectedIndexChanged -= listBox1_SelectedIndexChanged;
foreach (Curve curve in nullNameCurves)
listBox1.SetSelected(listBox1.Items.IndexOf(curve), false);
listBox1.SelectedIndexChanged += listBox1_SelectedIndexChanged;
}
According to MSDN, this event will be fired every time the selection changes:
If the SelectionMode property is set to SelectionMode.MultiSimple or SelectionMode.MultiExtended, any change to the SelectedIndices collection, including removing an item from the selection, will raise this event.
So basically, you can use it the same way as using it with single selection.
Sample:
For example if you want to deselect all items with null as newName:
foreach (var item in listBox1.SelectedItems)
{
if ((item as Curve).newName == null)
{
int index = listBox1.SelectedItems.IndexOf(item);
listBox1.SetSelected(index, false);
}
}
(I'm not sure if you can deselect items inside a foreach loop since it changes the SelectedItems object itself. If it does not work, you can still make a temporary list of those items and deselect them after the loop.)
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Curve curItem = null;
for (int i = 0; i < listBox1.SelectedItems.Count; i++)
{
curItem = (Curve)listBox1.SelectedItems[i];
if (curItem != null)
{
int index = listBox1.Items.IndexOf(curItem);
if (curItem.newName == null)
{
listBox1.SetSelected(index, false);
}
}
}
}