I'm using MultiselectList from Controls.Toolkit. I use it as a favourite selector. I have a list with items, I select the favourites and the next time I open the selection bar I would like to see my favourites already selected. When IsSelectionEnabledChanged event occurs, if IsSelectionEnabled is true (the selection bar is opened) I try to add my favourites to the list's SelectedItems. Here is a code snippet:
private void multiSelectList_IsSelectionEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (multiSelectList.IsSelectionEnabled)
{
foreach (var favourite in FavouritesList)
{
multiSelectList.SelectedItems.Add(multiSelectList.Items.Where(i => ((MyModel)i).id == favourite.id).FirstOrDefault());
}
}
}
I have tested this solution and I found out that the layout does not update the entire list that's why I'm not seeing the items as selected (but they are). Not even the actual visible items in the list. After scrolling for a bit and scrolling back, the selection appears! I've tried to use multiSelectList.UpdateLayout() method programatically but it did not solve it.
I wonder if it is a visualization problem or a CheckBox binding problem (the selection uses CheckBox on the side).
The SelectedItems is just a List<object>, it doesn’t raise any events when you update it.
To update your items manually, you could do something like following instead (untested code):
private void multiSelectList_IsSelectionEnabledChanged( object sender, DependencyPropertyChangedEventArgs e )
{
if( !multiSelectList.IsSelectionEnabled )
return;
var dictSelected = FavouritesList.ToDictionary( f => f.id, f => true );
for( int i = 0; i < multiSelectList.Items.Count; i++ )
{
MyModel m = (MyModel)multiSelectList.Items[ i ];
if( !dictSelected.ContainsKey( m.id ) )
continue; // Not selected
MultiselectItem item = (MultiselectItem)multiSelectList.ItemContainerGenerator.ContainerFromIndex( i );
if( null != item )
item.IsSelected = true; // This should add the item into the SelectedItems collection.
else
multiSelectList.SelectedItems.Add( m ); // The item is virtualized and has no visual representation yet.
}
}
Related
I need basically an event that triggers at each 200 records loaded, so more data can be loaded until the end of data.
I tried to extend CharmListCell and using the method updateItem like this:
#Override
public void updateItem(Model item, boolean empty) {
super.updateItem(item, empty);
currentItem = item;
if (!empty && item != null) {
update();
setGraphic(slidingTile);
} else {
setGraphic(null);
}
System.out.println(getIndex());
}
But the System.out.println(getIndex()); method returns -1;
I would like to call my backend method when the scroll down gets the end of last fetched block and so on, until get the end of data like the "infinite scroll" technique.
Thanks!
The CharmListCell doesn't expose the index of the underlying listView, but even if it did, that wouldn't be of much help to find out if you are scrolling over the end of the current list or not.
I'd suggest a different approach, which is also valid for a regular ListView, with the advantage of having the CharmListView features (mainly headers and the refresh indicator).
This short sample, created with a single view project using the Gluon IDE plugin and Charm 5.0.0, shows how to create a CharmListView control, and fill it with 30 items at a time. I haven't provided a factory cell, nor the headers, and for the sake of simplicity I'm just adding consecutive integers.
With a lookup, and after the view is shown (so the listView is added to the scene) we find the vertical ScrollBar of the listView, and then we add a listener to track its position. When it gets closer to 1, we simulate the load of another batch of items, with a pause transition that represents a heavy task.
Note the use of the refresh indicator. When new data is added, we scroll back to the first of the new items, so we can keep scrolling again.
public class BasicView extends View {
private final ObservableList<Integer> data;
private CharmListView<Integer, Integer> listView;
private final int batchSize = 30;
private PauseTransition pause;
public BasicView() {
data = FXCollections.observableArrayList();
listView = new CharmListView<>(data);
setOnShown(e -> {
ScrollBar scrollBar = null;
for (Node bar : listView.lookupAll(".scroll-bar")) {
if (bar instanceof ScrollBar && ((ScrollBar) bar).getOrientation().equals(Orientation.VERTICAL)) {
scrollBar = (ScrollBar) bar;
break;
}
}
if (scrollBar != null) {
scrollBar.valueProperty().addListener((obs, ov, nv) -> {
if (nv.doubleValue() > 0.95) {
addBatch();
}
});
addBatch();
}
});
setCenter(new VBox(listView));
}
private void addBatch() {
listView.setRefreshIndicatorVisible(true);
if (pause == null) {
pause = new PauseTransition(Duration.seconds(1));
pause.setOnFinished(f -> {
int size = data.size();
List<Integer> list = new ArrayList<>();
for (int i = size; i < size + batchSize; i++) {
list.add(i);
}
data.addAll(list);
listView.scrollTo(list.get(0));
listView.setRefreshIndicatorVisible(false);
});
} else {
pause.stop();
}
pause.playFromStart();
}
}
Note also that you could benefit from the setOnPullToRefresh() method, at any time. For instance, if you add this:
listView.setOnPullToRefresh(e -> addBatch());
whenever you go to the top of the list and drag it down (on a mobile device), it will make another call to load a new batch of items. Obviously, this is the opposite behavior as the "infinite scrolling", but it is possible as well with the CharmListView control.
I have a Listview that requires two API calls, one for all the text data, and one for image data for each item (via byte array). I first get all the text data and show the list, with a placeholder image. I then do a loop over all the items in the list view, and for each item I retrieve its image. I'd like the Listview to update after each image is retrieved, so that slowly the images will populate down the list, 1 by 1. However, the Listview only updates as Listview items are shown for the first time. For example, I'll load 20 items, with 7 being shown (the rest need to be scrolled to). If I wait a few seconds and scroll, the 8th+ items will show the updated images. Once all 20 items have retrieved their image, the entire list will update and show all the images.
I've tried setting the ItemSource (Results) of the listview to null after each image is retrieved, which calls OnPropertyChanged(), however the list still doesn't update for each item. Here's the code:
public async Task GetImagesForResults()
{
using (var scope = App.Container.BeginLifetimeScope())
{
using (new Busy(this))
{
var peopleService = scope.Resolve<IPersonService>();
_incompleteResults = Results;
if (_incompleteResults != null && _incompleteResults.Count > 0)
{
foreach(PersonViewModel p in _incompleteResults)
{
if (StopLoading)
{
break;
}
IsBusy = false;
var peopleImage = await peopleService.GetPersonImage(p.Email);
if ((peopleImage.Error == null) && (peopleImage.Response != null))
{
p.Picture = peopleImage.Response;
p.PictureImageSource = ImageSource.FromStream(() => new MemoryStream(peopleImage.Response));
p.ShowInitials = false;
Results = null;
Results = _incompleteResults;
}
}
}
}
}
}
Thanks for the help.
I'm using the following code to set the value of a textbox in a datagridview. The code does exactly what I want. However when I leave the current row, the value in CloseDate maintains the current date but discards the time and changes to "00:00:00".
The underlying SQL field is set to datetime. The DataType in the underlying data source is system.DateTime, DateTimeMode is set to UnspecifiedLocal.
What am I missing?
tbl_TransactionsDataGridView.CurrentRow.Cells["CloseDate"].Value = DateTime.Now;
From the EditingControlShowing action of my datagridview, I'm looking to see which field is being changed. In this instance I want 6.
private void tbl_TransactionsDataGridView_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
ComboBox combo = e.Control as ComboBox;
if (combo != null && tbl_TransactionsDataGridView.CurrentCell.ColumnIndex == 3)
{
// Remove an existing event-handler, if present, to avoid
// adding multiple handlers when the editing control is reused.
combo.SelectedIndexChanged -=
new EventHandler(ComboBox_SelectedIndexChanged);
// Add the event handler.
combo.SelectedIndexChanged +=
new EventHandler(ComboBox_SelectedIndexChanged);
}
if (combo != null && tbl_TransactionsDataGridView.CurrentCell.ColumnIndex == 6)
{
// Remove an existing event-handler, if present, to avoid
// adding multiple handlers when the editing control is reused.
combo.SelectedIndexChanged -=
new EventHandler(Status_SelectedIndexChanged);
// Add the event handler.
combo.SelectedIndexChanged +=
new EventHandler(Status_SelectedIndexChanged);
}
}
When the column index is 6, the following gets fired (I'm having some difficulties with getting this to fire correctly all the time, but thats an issue for another thread.). When it fires, it fires correctly and while I stay in the current row, CloseDate has the value I want it to. However, if I go to a new row and create a new record or select another record in the datagridview, CloseDate changes from "8/18/2014 8:42:32" to "8/18/2014 00:00:00".
private void Status_SelectedIndexChanged(object sender, EventArgs e)
{
object oStatus = new object();
oStatus = ((ComboBox)sender).SelectedValue;
if (Convert.IsDBNull(oStatus) && Convert.ToInt32(oStatus) == 1)
{
tbl_TransactionsDataGridView.CurrentRow.Cells["CheckOutEmployee"].Value = Environment.UserName;
tbl_TransactionsDataGridView.CurrentRow.Cells["CheckInEmployee"].Value = null;
tbl_TransactionsDataGridView.CurrentRow.Cells["CloseDate"].Value = null;
SendKeys.Send("{TAB}");
}
if (!Convert.IsDBNull(oStatus) && Convert.ToInt32(oStatus) > 1)
{
tbl_TransactionsDataGridView.CurrentRow.Cells["CheckInEmployee"].Value = Environment.UserName;
tbl_TransactionsDataGridView.CurrentRow.Cells["CloseDate"].Value = DateTime.Now;
SendKeys.Send("{TAB}");
}
}
I'm looking for a simple way to loop through all buttons onscreen for a given tag. Eg "foo". I'm using WP7, using C#. I'm very new to the platform so go easy on me :P
Googling this sort of thing isn't really working out for me either - I think I have my terminology wrong, so any tips on that too would be appreciated.
You should probably loop through all the controls on your page, check whether each one is a button, and if so check its Tag property.
Something like this...
foreach (UIElement ctrl in ContentPanel.Children)
{
if (ctrl.GetType() == typeof(Button))
{
Button potentialButton = ((Button)ctrl);
if (potentialButton.Tag = Tag)
return (Button)ctrl;
}
}
Bear in mind, though, that if you have nested controls on the page, you will need to think about recursing into any item with children to make sure you catch all the controls.
First, create a method to enumerate recursively the controls in your page:
public static IEnumerable<FrameworkElement> FindVisualChildren(FrameworkElement control)
{
if (control == null)
{
yield break;
}
for (int i = 0; i < System.Windows.Media.VisualTreeHelper.GetChildrenCount(control); i++)
{
var child = System.Windows.Media.VisualTreeHelper.GetChild(control, i) as FrameworkElement;
if (child != null)
{
yield return child;
foreach (var grandChild in FindVisualChildren(child))
{
yield return grandChild;
}
}
}
}
Then call it and keep only the controls you want:
var buttons = FindVisualChildren(this.ContentPanel)
.OfType<Button>()
.Where(b => b.Tag is string && (string)b.Tag == "foo");
(where ContentPanel is the root element of your page)
I want to create a wizard control from the pivot control. To achieve this I need to stop the pivot looping. I want to stop the pivot control moving forward from the last item to the first and backwards from the first to the last.
I'm pretty sure I should be able to intercept the manipulations e.g. cancel if I detect a right to left manipulation on the last pivot item. I can capture this in ManipulationDelta but don't know how to cancel the manipulation.
I have tried setting e.Handled = True but it didn't work.
I tried to set IsHitTestVisisble to false but this kills all manipulations. I tried setting it back to true in ManipulationCompleted but this then allows all manipulations.
Any ideas?
Cheers
Steve
The pivot is not designed to be used as a wizard and does not support stopping it's looping behaviour as this would create an inconsistent UX for users.
If you really must create a wizard do it with multiple pages.
Don't use a Pivot for a Wizard. Create your own transitions instead.
I couldn't reply to your comment on Matts answer but I just wanted to point you to this:
http://forty3degrees.wordpress.com/2011/07/19/creating-a-swipable-contentcontrol/
It's the last entry in my very neglected blog and should provide a good base for creating a wizard using a pivot style swipe.
Calum.
EDIT: I tried to do what you wanted with the pivot but couldn't find a way to stop it looping. The only way that I can think of to achieve this would be to derive a custom control from Pivot. Unfortunately SelectedIndex/SelectedItem are not virtual so you would need to hide them (with the new modifier) and reproduce the logic from the base class.
It's just an alternative solution I've posted here - you can try to make use of XNA framework TouchPanel and Touch.FrameReported Event:
using Microsoft.Xna.Framework.Input;
public MainPage()
{
InitializeComponent();
myPivot.IsHitTestVisible = false; // disable your Pivot
Touch.FrameReported += Touch_FrameReported;
TouchPanel.EnabledGestures = GestureType.HorizontalDrag;
}
TouchPoint first;
private void Touch_FrameReported(object sender, TouchFrameEventArgs e)
{
TouchPoint mainTouch = e.GetPrimaryTouchPoint(this);
if (mainTouch.Action == TouchAction.Down)
first = mainTouch;
else if (mainTouch.Action == TouchAction.Up && TouchPanel.IsGestureAvailable)
{
if (mainTouch.Position.X < first.Position.X)
{
if (myPivot.SelectedIndex < myPivot.Items.Count - 1)
myPivot.SelectedIndex++;
}
else if (mainTouch.Position.X > first.Position.X)
{
if (myPivot.SelectedIndex > 0)
myPivot.SelectedIndex--;
}
}
}
Thought it would probably work from WP7.1 as TouchPanel is available from that version of the OS.
If you absolutely want to keep the Pivot from looping, here is a quick and dirty hack:
int previousSelectedIndex = 0;
public PageWithPivot()
{
InitializeComponent();
pivot.SelectionChanged += new SelectionChangedEventHandler(pivot_SelectionChanged);
}
private void pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (pivot.SelectedIndex == 0 && previousSelectedIndex == <number Of screens - 1>)
pivot.SelectedIndex = <number Of screens - 1>;
previousSelectedIndex = pivot.SelectedIndex;
}
This causes your PivotControl to jump back to the last pivotItem. Not very pretty but works.
This is so weird because it only works in the Emulator. I guess you shan't mess with the UI
You can use MVVM:
<phone:Pivot Foreground="Black"
Name="pivot1"
Title="AIDE"
SelectedIndex="{Binding SelectedItem, Mode=TwoWay}">
Cs:
private class HelpViewModel : ViewModelBase
{
public HelpViewModel()
{
}
private int _SelectedItem = 0;
public int SelectedItem
{
get
{
return _SelectedItem;
}
set
{
if (_SelectedItem != value)
{
if (value == 3)
_SelectedItem = 0;
else
_SelectedItem = value;
RaisePropertyChanged(() => SelectedItem);
}
}
}
}
public AppHelpPivot()
{
InitializeComponent();
LayoutRoot.DataContext = new HelpViewModel();
}