Load the listview item 10 by 10 when scrolling - xamarin.forms.listview

I am using xamarin.forms.
I have listview with 100 items. I want to show only 10 items initially when loading. When scrolling upto 10 item, then next 10 item should be loaded with activityindicator. Can anyone help me to resolve this issue.

use ListView.ItemAppearing, for example:
var listView = new ListView { ... };
listView.ItemAppearing += async (sender, e) =>
{
var items = listView.ItemsSource as IList;
if (items == null
|| items.Count == 0)
return;
if (e.Item != items[items.Count - 1])
return;
System.Diagnostics.Debug.WriteLine("The end of the list, load more!");
};

Related

Notification when a ListViewItem scrolls into view

The following code is supposed to scroll an item into view and set focus to the first child control in the template:
lv.ScrollIntoView(lv.SelectedItem);
var lvi = lv.SelectedListViewItem();
//get the item's template parent
var templateParent = lvi.GetFrameworkElementByName<ContentPresenter>();
if (templateParent != null) <--but it's always null
{
var ctrl = templateParent.FindVisualChildren<FrameworkElement>().First();
ctrl.Focus();
}
The problem is that if the ListViewItem is not visible, then templateParent is null, and this code doesn't work. And of course this code is only useful when the item isn't already visible.
Is there a way to scroll the item into view and then be notified when it has come into view so that the template will be non-null so that the ctrl.Focus() code would execute?
You could handle the RequestBringIntoView event. Please refer to the following sample code.
public MainWindow()
{
InitializeComponent();
lv.ItemsSource = Enumerable.Range(1, 100);
lv.SelectedItem = 90;
lv.ScrollIntoView(lv.SelectedItem);
lv.RequestBringIntoView += Lv_RequestBringIntoView;
}
private void Lv_RequestBringIntoView(object sender, RequestBringIntoViewEventArgs e)
{
var container = lv.ItemContainerGenerator.ContainerFromItem(lv.SelectedItem);
if (container != null)
{
//...
}
}

How to clear selected items collection

I have a WPF Application with two ComboBox
When I select the first one the items related to the first combobox will be populated on the second one
Here is my select Property
public string SelectedApplication
{
set
{
if (_selectedApplication == value) return;
this._selectedApplication = value;
InitializeTransactionTypes();
}
get
{
return this._selectedApplication;
}
}
here i am checking a matching id between the two comboboxes to populate the second combobox items.
ObservableCollection<TransactionTypeViewModel> _transTypeObsList = new ObservableCollection<TransactionTypeViewModel>();
private void InitializeTransactionTypes()
{
if (_selectedApplication != null)
{
var getAppCode =
ApplicationVModel.GetAllApplications()
.FirstOrDefault(apps => apps.Name == _selectedApplication);
var transTypeList = TransactionTypeVModel.GetAllViewModelTransTypes()
.Where(t => getAppCode != null && t.Id == getAppCode.Id);
transactionTypes = new ObservableCollection<TransactionTypeViewModel>(transTypeList);
NotifyPropertyChanged("TransactionTypes");
}
}
More information about methods:
List of VM mapped from List of Model
public List<TransactionTypeViewModel> GetAllViewModelTransTypes()
{
TransactionTypeViewModels = TransactionTypeModel.GetAllTransactionTypes().Select(transType => new TransactionTypeViewModel
{
Id = transType.Id,
Name = transType.Name,
})
.ToList();
return TransactionTypeViewModels;
}
Lets say I select first combobox has {A,B,C,D} ...and the second combobox has {A'1,A'2,A'3}, when I select item from first Combobox the second combobo keeps populating
items. I wanted to show only {A'1 for A} {B'1 for B} ...etc but now what it does is {A'1 A'1 A'1 ..... for A} {B'1 B'1 B'1 ....for B} for every select.
I want the previous selection to be cleared and display a new list per select. Thanks
To sum up comments. Instead of creating new ObservableCollection every time something changes reuse one that you already have and it will notify UI about changes. Your InitializeTransactionTypes should look something like this:
private void InitializeTransactionTypes()
{
if (_selectedApplication != null)
{
var getAppCode =
ApplicationVModel.GetAllApplications()
.FirstOrDefault(apps => apps.Name == _selectedApplication);
transactionTypes.Clear();
foreach(var transactionItem in TransactionTypeVModel.GetAllViewModelTransTypes().Where(t => getAppCode != null && t.Id == getAppCode.Id))
transactionTypes.Add(transactionItem);
}
}
and like this you should not have to notify about TransactionTypes changes any more

Datagrid Column Collection Changed Event

I use this code to add or remove column from datagrid. each column header I have mouse enter and leave event. For new column I also would like to add the same event handler after inserting to datagrid.
private void Columns_CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs e)
{
if (e.Action == CollectionChangeAction.Add)
{
int columnPosition = (this.Columns.Count - 1);
DataGridTextColumn column = new DataGridTextColumn();
column.Header = (e.Element as DataColumn).ColumnName;
column.Binding = new Binding(string.Format("[{0}]", column.Header.ToString()));
this.Columns.Insert(columnPosition, column);
DataGridColumnHeader columnHeader = DataGridHelper.GetColumnHeader(this, columnPosition);
if (columnHeader != null)
{
columnHeader.MouseEnter += new MouseEventHandler(ColumnHeader_MouseEnter);
columnHeader.MouseLeave += new MouseEventHandler(ColumnHeader_MouseLeave);
}
SetAutomappingOnOff = false;
}
else if (e.Action == CollectionChangeAction.Remove)
{
DataColumn column = e.Element as DataColumn;
DataGridColumn toRemove = (from DataGridColumn dc in this.Columns
where dc.Header != null && dc.Header.ToString() == column.ColumnName
select dc).First();
this.Columns.Remove(toRemove);
SetAutomappingOnOff = false;
}
}
< Edit>
DataGridHelper
public static class DataGridHelper
{
public static DataGridColumnHeader GetColumnHeader(DataGrid dataGrid, int index)
{
DataGridColumnHeadersPresenter presenter = FindVisualChild<DataGridColumnHeadersPresenter>(dataGrid);
if (presenter != null) {
return (DataGridColumnHeader)presenter.ItemContainerGenerator.ContainerFromIndex(index)‌​;
}
return null;
}
}
< /Edit>
But columnHeader always returns null even though I can see that object is created and added to datagrid.
Pls help me.
Thanks
Dee
While the column has been added to the DataGrid, it hasn't yet been added to the VisualTree so your FindVisualChild method is returning null. I don't have a good solution for adding the click handler for the column, but you could add it to the DataGrid and check the sender to see where to apply the click handling logic.
I would suggest registering CollectionChanged event on DataGrid-s Loaded event. That way you can be sure that DataGridColumnHeader is added to the visual tree. It will look like this:
myDataGrid.Loaded += (s,e) => {
myCollection.CollectionChanged += (se, ev) => {
//do work here
};
};

MVVMLight Multiple Listbox Selection

I have an ItemsContol bound to a Country model - which look like this.
Country
--int Id
--string Name
--List Counties
In the DataTemplate of the ItemsControl there's a Listbox - which is bound to the Counties property.
So what I want is only one item in any of the listboxes be selected at any one time.
For example:
I have an item selected in the first listbox and I click an item in the second listbox, then the first listbox shouldn't have any selected items.
Any ideas ??
Add a SelectedCounty property to your Country object. Then you can bind the SelectedItem on your ListBox to that property. Then in code manually set all others to null. Something like so
Country.OnPropertyChanged += (s,e) =>
{
if(e.PropertyName == "SelectedCounty")
{
foreach(Country country in MyCountries)
if(country != sender)
country.SelectedCounty = null;
}
}
Just for reference here's the solution I'm using - it resides in the CountryViewModel
private CountyModel _selectedcounty;
public CountyModel SelectedCounty
{
get { return _selectedcounty; }
set
{
_selectedcounty = value;
RaisePropertyChanged("SelectedCounty");
if (value != null)
{
if (CountySelectedEvent != null)
CountySelectedEvent(value, EventArgs.Empty);
Messenger.Default.Send<CountyModel>(value, "SelectedCounty");
}
}
}
public CountryViewModel()
{
Counties = new ObservableCollection<CountyModel>();
Messenger.Default.Register<CountyModel>(this, "SelectedCounty",
msg =>
{
if(msg != this.SelectedCounty && msg != null)
this.SelectedCounty = null;
});
}
Hope it helps someone :)

Generic method to find all TextBox controls in Silverlight

I have several Silverlight controls on a page and want query all the controls that are of type TextBox and have that working.
Now the Silverlight form I'm working on could have more TextBox controls added. So when I test to see if a TextBox control has a value, I could do:
if (this.TextBox.Control.value.Text() != String.Empty)
{
// do whatever
}
but I'd rather have if flexible that I can use this on ANY Silverlight form regardless of the number of TextBox controls I have.
Any ideas on how I would go about doing that?
I have already faced this issue and notify it here : http://megasnippets.com/en/source-codes/silverlight/Get_all_child_controls_recursively_in_Silverlight
Here you have a generic method to find recursively in the VisualTree all TextBoxes:
IEnumerable<DependencyObject> GetChildrenRecursively(DependencyObject root)
{
List<DependencyObject> children = new List<DependencyObject>();
children.Add(root);
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++)
children.AddRange(GetChildrenRecursively(VisualTreeHelper.GetChild(root, i)));
return children;
}
Use this method like this to find all TextBoxes:
var textBoxes = GetChildrenRecursively(LayoutRoot).OfType<TextBox>();
It sounds like you need a recursive routine like GetTextBoxes below:
void Page_Loaded(object sender, RoutedEventArgs e)
{
// Instantiate a list of TextBoxes
List<TextBox> textBoxList = new List<TextBox>();
// Call GetTextBoxes function, passing in the root element,
// and the empty list of textboxes (LayoutRoot in this example)
GetTextBoxes(this.LayoutRoot, textBoxList);
// Now textBoxList contains a list of all the text boxes on your page.
// Find all the non empty textboxes, and put them into a list.
var nonEmptyTextBoxList = textBoxList.Where(txt => txt.Text != string.Empty).ToList();
// Do something with each non empty textbox.
nonEmptyTextBoxList.ForEach(txt => Debug.WriteLine(txt.Text));
}
private void GetTextBoxes(UIElement uiElement, List<TextBox> textBoxList)
{
TextBox textBox = uiElement as TextBox;
if (textBox != null)
{
// If the UIElement is a Textbox, add it to the list.
textBoxList.Add(textBox);
}
else
{
Panel panel = uiElement as Panel;
if (panel != null)
{
// If the UIElement is a panel, then loop through it's children
foreach (UIElement child in panel.Children)
{
GetTextBoxes(child, textBoxList);
}
}
}
}
Instantiate an empty list of TextBoxes. Call GetTextBoxes, passing in the root control on your page (in my case, that's this.LayoutRoot), and GetTextBoxes should recursively loop through every UI element that is a descendant of that control, testing to see if it's either a TextBox (add it to the list), or a panel, that might have descendants of it's own to recurse through.
Hope that helps. :)
From your top most panel you can do this (my grid is called ContentGrid)
var textBoxes = this.ContentGrid.Children.OfType<TextBox>();
var nonEmptyTextboxes = textBoxes.Where(t => !String.IsNullOrEmpty(t.Text));
foreach (var textBox in nonEmptyTextboxes)
{
//Do Something
}
However this will only find the textboxes that are immediate children. Some sort of recursion like below would help, but I'm thinking there must be a better way.
private List<TextBox> SearchForTextBoxes(Panel panel)
{
List<TextBox> list = new List<TextBox>();
list.AddRange(panel.Children.OfType<TextBox>()
.Where(t => !String.IsNullOrEmpty(t.Text)));
var panels = panel.Children.OfType<Panel>();
foreach (var childPanel in panels)
{
list.AddRange(SearchForTextBoxes(childPanel));
}
return list;
}
Took Scott's initial idea and expanded it so that it
Uses generics, so it easily copes with multiple control types.
Supports more container types. In my WP7 I needed to support panaorama's, scroll viewers etc... which aren't Panels. So this allows support for them.
Biggest issue is that string comparing, especially on the Panel and derrived items.
Code:
private static void GetControls<T>(UIElement uiElement, List<T> controlList) where T : UIElement
{
var frameworkFullName = uiElement.GetType().FullName;
if (frameworkFullName == typeof(T).FullName)
{
controlList.Add(uiElement as T);
return;
}
if (frameworkFullName == typeof(Panel).FullName ||
frameworkFullName == typeof(Grid).FullName ||
frameworkFullName == typeof(StackPanel).FullName)
{
foreach (var child in (uiElement as Panel).Children)
{
GetControls(child, controlList);
}
return;
}
if (frameworkFullName == typeof(Panorama).FullName)
{
foreach (PanoramaItem child in (uiElement as Panorama).Items)
{
var contentElement = child.Content as FrameworkElement;
if (contentElement != null)
{
GetControls(contentElement, controlList);
}
}
return;
}
if (frameworkFullName == typeof(ScrollViewer).FullName)
{
var contentElement = (uiElement as ScrollViewer).Content as FrameworkElement;
if (contentElement != null)
{
GetControls(contentElement, controlList);
}
return;
}
}
Similar logic to ideas above to also handle controls with a "Content" attribute like TabItems and Scrollviewers where children might be embedded at a lower level. Finds all children:
IEnumerable<DependencyObject> GetControlsRecursive(DependencyObject root)
{
List<DependencyObject> elts = new List<DependencyObject>();
elts.Add(root);
string type = root.GetType().ToString().Replace("System.Windows.Controls.", "");
switch (root.GetType().ToString().Replace("System.Windows.Controls.", ""))
{
case "TabItem":
var TabItem = (TabItem)root;
elts.AddRange(GetControlsRecursive((DependencyObject)TabItem.Content));
break;
case "ScrollViewer":
var Scroll = (ScrollViewer)root;
elts.AddRange(GetControlsRecursive((DependencyObject) Scroll.Content));
break;
default: //controls that have visual children go here
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++) elts.AddRange(GetControlsRecursive(VisualTreeHelper.GetChild(root, i)));
break;
}
return elts;
}

Resources