I get a Catastrophic failure when a ListViews ItemSource - ObservableCollection gets loaded with less items, when using a Double Tap event. Now a wrote a very simple example of this, and I still get the same error.
This is a Windows 8.1 Store Application.
Simple Example: If I have 6 items in the listview, and I double tap item number 3. Then the ListView rebinds with 4 items. This works perfect.
But if I double tap item number 5 or 6 I get a Catastrophic failure.
Code:
<ListView x:Name="myListView" DoubleTapped="myListView_DoubleTapped" />
public MainPage()
{
this.InitializeComponent();
List<string> list1 = new List<string>();
list1.Add("item1");
list1.Add("item2");
list1.Add("item3");
list1.Add("item4");
list1.Add("item5");
list1.Add("item6");
myListView.ItemsSource = list1;
}
private void myListView_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
List<string> list2 = new List<string>();
list2.Add("OterhItem1");
list2.Add("OterhItem2");
list2.Add("OterhItem3");
list2.Add("OterhItem4");
myListView.ItemsSource = list2;
}
Update: I tried the same but with only a Tapped event, and it works fine. So it looks like this only happens with the doubletapped event.
I'm pretty sure that the problem is caused by animating in new values, as I'm getting a similar thing in my app.
Frame fr = Window.Current.Content as Frame;
fr.IsEnabled = false;
UpdateCollectionSomehow(...)
fr.IsEnabled = true;
In my code, I just disable the code before adding any more elements, and the problem went away.
Related
I am making a UWP app which is supposed to be on xbox for now and maybe in future ill release it on pc and other platforms. I know that on PC and for mobile we can enable this feature with following 2 properties on the GridView or ListView.
CanReorderItems=True
CanDrop=True
But according to Microsoft Docs, drag and drop feature is not available or supported on xbox.
So what are any other options to achieve this reorder feature on xbox GridView?
UPDATE 1
So here is my backend code for the gridview. selection mode is single but I am not using selectionchanged event because that just creates lot of confusion and for now just assume that we always need to swap the items I will set the boolean later once the swapping in working perfectly.
private void SamplePickerGridView_ChoosingItemContainer(Windows.UI.Xaml.Controls.ListViewBase sender, ChoosingItemContainerEventArgs args)
{
if (args.ItemContainer != null)
{
return;
}
GridViewItem container = (GridViewItem)args.ItemContainer ?? new GridViewItem();
//should be xbox actually after pc testing
if (DeviceTypeHelper.GetDeviceFormFactorType() == DeviceFormFactorType.Desktop)
{
container.GotFocus += Container_GotFocus;
container.LostFocus += Container_LostFocus;
//container.KeyDown += Container_KeyDown;
}
args.ItemContainer = container;
}
private TVShow GotItem, LostItem;
private void Container_LostFocus(object sender, RoutedEventArgs e)
{
LostItem = OnNowAllGridView.ItemFromContainer(e.OriginalSource as GridViewItem) as TVShow;
GotItem = null;
}
private void Container_GotFocus(object sender, RoutedEventArgs e)
{
GotItem = OnNowAllGridView.ItemFromContainer(e.OriginalSource as GridViewItem) as TVShow;
if (GotItem != null && LostItem != null)
{
var focusedItem = GotItem;
var lostitem = LostItem;
var index1 = ViewModel.Source.IndexOf(focusedItem);
var index2 = ViewModel.Source.IndexOf(lostitem);
ViewModel.Source.Move(index1, index2);
}
LostItem = null;
}
u can try the code with adaptivegridview or just normal gridview of uwp if it works with that it should work with adaptivegridview as well.
Current Bheaviour items are swaped but the focus remains at same index.
Expected the focus should also move along with the item.
Your finding is true, drag and drop is not supported on Xbox out of the box (although when mouse support comes to Xbox in the future, I guess it will work).
So if you need this functionality, you will have to implement it manually from the start. One option would be to add a button, that will display on Xbox only and will read like Reorder Grid.
When this "reorder" mode were enabled, you have several solutions available.
The easiest solution for you would be to set the SelectionMode to Single and when a item is selected, you would bring it to fromt of the underlying collection.
collection.Remove( selectedItem );
collection.Insert( 0, selectedItem );
This bring to front solution was implemented on the Xbox One dashboard for reordering tiles.
Second option would be to set the SelectionMode to Multiple, where user would first select one item and then a second one. After that you could move the first selected item before the second selected:
collection.Remove( firstSelectedItem );
var targetIndex = collection.IndexOf( secondSelectedItem );
collection.Insert( targetIndex, firstSelectedItem );
The last solution is the most complex. With SelectionMode = Single you would select a single item and then observe the direction in which the user focus moves and move the tile "in real time". This is the most user friendly, but hardest to implement reliably.
Just as an outline of the third solution - you could capture the GotFocus event if you implement a custom template of the GridView:
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal"
GotFocus="GridViewItem_GotFocus"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
Now within this GotFocus handler you could retrieve the item that has currently focus from the EventArgs.OriginalSource. This way you could know which item got the focus and you could swap it with the item the user selected.
Update - hacky solution
I have come up with a hacky approach that solves the GotFocus/LostFocus mess.
The problem with GotFocus is that when we move the item in collection, the focus gets confused. But what if we didn't physically move the items at all?
Suppose your item type is TVShow. Let's create a wrapper around this type:
public class TVShowContainer : INotifyPropertyChanged
{
private TVShow _tvShow;
public TVShow TvShow
{
get => _tvShow;
set
{
_tvShow = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Now change the collection item type to this new "wrapper" type. Of course, you also have to update your GridView DataTemplate to have the right references. Instead of "{Binding Property}" you will now need to use "{Binding TvShow.Property}", or you can set the DataContext="{Binding TvShow}" attribute to the root element inside the DataTemplate.
But you may now see where I am going with this. Currently you are using Move method to move the items in the collection. Let's replace this with a swap:
var item1 = focusedItem.TvShow;
focusedItem.TvShow = LostItem.TvShow;
LostItem.TvShow = item1;
This is a big difference, because we no longer change the collection itself, but just move the references to items that are wrapped in a "static" container. And thanks to bindings the items will properly display where they should.
This is still a hacky solution, because it requires you to wrap your items just for the sake of the reordering, but it at least works. I am however still interested in finding a better way to do this.
Hi I am trying to create a context menu in windows 8 using Popup. On Right click of a button I am calling the following function
private async void UIElement_OnRightTapped(object sender, RightTappedRoutedEventArgs e)
{
PopupMenu popUpMenu = new PopupMenu();
popUpMenu.Commands.Add(new UICommand("File"));
Rect rect = GetRect(sender);
var result= await popUpMenu.ShowForSelectionAsync(rect, Placement.Right);
}
While defination for GetRect method is as follows:-
private Rect GetRect(object sender)
{
FrameworkElement element = sender as FrameworkElement;
GeneralTransform elementTransform = element.TransformToVisual(null);
Point point = elementTransform.TransformPoint(new Point());
Size size = new Size(element.ActualWidth, element.ActualHeight);
Rect rect = new Rect(point, size);
return rect;
}
Though the GetRect is returning correct value, but still i am getting result as null.
Please help
I can't figure out why you have this problems. On the first view, everything looks fine.
Maybe Tim Heuer Callisto package contains an easier approach for exactly what you want. See this example.
It's because you're not clicking on the File command, but instead clicking elsewhere on the page.
Working on a Metro style app in C#. I have a custom control which inherits from Grid. MyGrid contains some other custom controls. I'm trying to do hit testing on those controls in the PointerReleased handler:
void MyGrid_PointerReleased(object sender, PointerRoutedEventArgs e)
{
PointerPoint pt = e.GetCurrentPoint(this);
var hits = VisualTreeHelper.FindElementsInHostCoordinates(pt.Position, this);
int breakhere = hits.Count();
}
After this code executes, hitCount is 0. If I move the PointerReleased handler one control higher in the visual tree heirarchy then hitCount is correct the first time and 0 after that. I set up a test project with similar XAML layout to try to reproduce the problem and it works correctly every time. So I'm not sure what bad thing I have done that is preventing VisualTreeHelper from working. I'm not really sure how to proceed debugging this. Any ideas what would cause this function to return no results?
I'm wanting to print a UserControl that binds to a view model. I create the control in code because if I don't it prints off the edge of the page. Apparently, there is no way to control the size or scale of a printed UIElement without screwing up the element that is on the Silverlight page. So I create the 'UserControl' with the following code in the PrintPage event of a PrintDocument:
private void OnPrintPage(object sender, PrintPageEventArgs e)
{
PurchaseReceipt purchaseReceipt = new PurchaseReceipt();
purchaseReceipt.DataContext = _receiptData;
purchaseReceipt.Visibility = System.Windows.Visibility.Visible;
purchaseReceipt.Margin = new Thickness(25.0);
purchaseReceipt.Width = e.PrintableArea.Width - 50.0;
e.PageVisual = purchaseReceipt;
}
The problem is that binding doesn't work, or doesn't work in time. Is there a way to force an element to bind?
I'm trying to speed up my windows phone 7 page load times. I have a 'static' page that has a dynamically created in a Panorama control - static meaning that the content never changes.
On the first load I look at my config file, create the individual PanoramaItem controls and add them to the main Panorama control. I'm trying to keep a List in a static place so that the initial creation would only happen once and I could just add a fully rendered version to my Panorama control when the page was rendered.
Works fine on first load, but when I try to add the cached PanoramaItems to the Panorama control I get the message "Element is already the child of another element". This makes sense since I already added before. But I can see a way to disconnect the PanoramaItems with the first Panorama control...
I could be going about the control caching thing all wrong as well... Let me know if there's another way to do this.
You can use Panorama.Items.Remove(pivotItem) for this
As an example
With the following page fields
PanoramaItem pi;
bool blahShown = false;
On the press of this button, the control is first instantiated and displayed and on subsequent presses removed and readded without instantiation.
private void button1_Click(object sender, RoutedEventArgs e)
{
if (pi == null) {
pi = new PanoramaItem();
pi.Header = "blah";
}
if (blahShown) {
Pano.Items.Remove(pi);
blahShown = false;
} else {
Pano.Items.Add(pi);
blahShown = true;
}
}