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);
}
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;
}
}
}
}
Good day
I must be missing something really simple here; I have a datagrid with two combobox columns and a datagridtextcolumn. Once the user has selected a value for both of these comboboxes (a value ranging from 1 to 5), a calculation is done with these 2 values (on a combobox selection changed event). I need to write the result of the calculation in to a cell (the datagridtextcolumn) and change the background of that cell to a specific colour based on the value. I have managed to find the values in the comboboxes and the index of the current row, but accessing the relevant cell to write the result to is getting the better of me...
Here is the coding for the event handler that calls the calculation thus far:
private void cmbConseq_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var cellInfos = dgDeviation.SelectedCells;
if (cellInfos == null)
{
return;
}
var content = cellInfos[0].Column.GetCellContent(cellInfos[0].Item);
DataRowView row = (DataRowView)content.DataContext;//get the datacontext from FrameworkElement and typecast to DataRowView
object[] obj = row.Row.ItemArray;//ItemArray returns an object array with single element
int likely = Convert.ToInt16(obj[0]);
int cons = Convert.ToInt16(obj[1]);
int riskValue = new Calculations().riskRankingCalc(likely, cons);
int rowIndex = dgDeviation.Items.IndexOf(dgDeviation.CurrentItem);
How do I write the 'riskValue' variable to the relevant cell in the currently selected row in the eventhandler?
After a bit of experimentation and help from other SO questions, the solution that worked for me was to implement the following two helper methods:
public static T GetVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
public static DataGridCell GetCell(DataGrid grid, int column)
{
DataGridRow row = grid.ItemContainerGenerator.ContainerFromIndex(grid.SelectedIndex) as DataGridRow;
if (row != null)
{
DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(row);
if (presenter == null)
{
grid.ScrollIntoView(row, grid.Columns[column]);
presenter = GetVisualChild<DataGridCellsPresenter>(row);
}
DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
return cell;
}
return null;
}
And then to make the change in the cell is as easy as calling: GetCell(myDatagrid, columnIndex).Content = "New Value";
I have a DataGrid where the ItemsSource is bound to an ObservableCollection<LogEntry>. On a click on a Button the user can scroll to a specific LogEntry. Therefor I use the following code:
private void BringSelectedItemIntoView(LogEntry logEntry)
{
if (logEntry != null)
{
ContentDataGrid.ScrollIntoView(logEntry);
}
}
This just works fine. But what I don't like is: If the LogEntry already is in view then the DataGrid flickers shortly.
My question now is:
Is there a possibility to check on the DataGrid if the given LogEntry already is in view?
You can get index for first visible item and last visible item
then you can check if your item's index was within first and last or not.
var verticalScrollBar = GetScrollbar(DataGrid1, Orientation.Vertical);
var count = DataGrid1.Items.Count;
var firstRow = verticalScrollBar.Value;
var lastRow = firstRow + count - verticalScrollBar.Maximum;
// check if item index is between first and last should work
Get Scrollbar method
private static ScrollBar GetScrollbar(DependencyObject dep, Orientation orientation)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dep); i++)
{
var child = VisualTreeHelper.GetChild(dep, i);
var bar = child as ScrollBar;
if (bar != null && bar.Orientation == orientation)
return bar;
else
{
ScrollBar scrollBar = GetScrollbar(child, orientation);
if (scrollBar != null)
return scrollBar;
}
}
return null;
}
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
}
I have a tabcontrol with 9 tabitems in it. Each tab has as series of TextBoxes for the user to enter data. At the bottom is a clear button, hooked up to this method:
public void ClearTextBoxes()
{
ChildControls ccChildren = new ChildControls();
foreach (object o in ccChildren.GetChildren(rvraDockPanel, 2))
{
if (o.GetType() == typeof(WatermarkTextBox) || o.GetType() == typeof(DateBox) ||
o.GetType() == typeof(DigitBox) || o.GetType() == typeof(PhoneBox))
{
WatermarkTextBox water = (WatermarkTextBox)o;
water.Text = "";
}
else if (o.GetType() == typeof(ComboBox))
{
ComboBox combo = (ComboBox)o;
combo.SelectedIndex = -1;
}
else if (o.GetType() == typeof(CheckBox))
{
CheckBox check = (CheckBox)o;
check.IsChecked = false;
}
}
}
This works perfectly fine, however I also have a MenuItem that allows the user to ClearAll tabs. Right now the clear button only clears what's on the currently selected tab, and leaves everything else alone. My thought on how to do this was to iterate through the tabitems with this loop:
for (int i = 0; i < 10; i++ )
{
tabSelection.SelectedIndex = i;
clearButton_Click(null, null);
}
It will flip through all the tabs, but won't clear anything. I have tried using Automation instead, with the same result. It just won't seem to clear anything.
ChildControls class:
class ChildControls
{
private List<object> listChildren;
public List<object> GetChildren(Visual p_vParent, int p_nLevel)
{
if (p_vParent == null)
{
throw new ArgumentNullException("Element {0} is null!", p_vParent.ToString());
}
this.listChildren = new List<object>();
this.GetChildControls(p_vParent, p_nLevel);
return this.listChildren;
}
private void GetChildControls(Visual p_vParent, int p_nLevel)
{
int nChildCount = VisualTreeHelper.GetChildrenCount(p_vParent);
for (int i = 0; i <= nChildCount - 1; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(p_vParent, i);
listChildren.Add((object)v);
if (VisualTreeHelper.GetChildrenCount(v) > 0)
{
GetChildControls(v, p_nLevel + 1);
}
}
}
}
WPF discards the entire VisualTree of a TabItem which isn't selected, so no controls actually exist on tabs that are not visible.
To maintain control values (such as Selections, Text, etc), they need to be bound to something. If they're bound to something then your Clear() method should actually clear the Bound values, not the UI values. If they're not bound to anything, then the TabControl will revert to its initial state when selected.
To get your code working you need to add the following line to your cleanup method:
tabControl.SelectedIndex = i;
--> UpdateLayout();
Button_Click(null, null);
The UpdateLayout method takes care that the TabItem is drawn and the VisualTree is available afterwards.
Generally this approach isn't nice and if you have a real Application with Business data behind I'd recommend you have a look at databinding/MVVM. The approach shouldn't be to reset your controls in the view, but to reset your bound business data in the background.