I am just getting started on Windows phone 7 development and am stuck at this problem while using Pivot control:
I have 3 pivotitems and the swipe movement to navigate between pivots are working fabulously well, But the problem is...
I need to call a different function, say function1() when one pivotitem is visible and then call a function say function2() as soon as user swipes to another pivotitem.
Is there any delegate method which handles this..?
Thanks for your help!
You can handle the Pivot control's LoadingPivotItem event. This event passes PivotItemEventArgs which includes a property letting you know what pivot is about to be shown. Using this, you can then load the relevant controls and properties. For example,
private void pivotMain_LoadingPivotItem(object sender, PivotItemEventArgs e)
{
if (e.Item == pivotItem1)
{
//Load Pivot Item 1 stuff
}
if (e.Item == pivotItem2)
{
//Load Pivot Item 2 stuff
}
}
In the example above, pivotItem1 and pivotItem2 are the names I've given each PivotItem so you can give whatever names you want to each PivotItem and check if they're about to be shown. If you want to handle the event after the PivotItem has loaded, you can use the Pivot.LoadedPivotItem method.
If you want to know which PivotItem is currently being displayed at any time, you can use the Pivot.SelectedIndex method. It's zero-based, so the first PivotItem will have an index of 0, the second will have 1 and so on.
You can use SelectionChanged. In this function you'll be able to check to see which PivotItem is the SelectedItem and choose which function you want to call.
Related
In my application, I need to select the newly created document(note) when I go back to library. After library item is selected, the Library must be scrolled to the selected item.
My library's OnLoaded method:
private async void OnLoaded(object sender, RoutedEventArgs e)
{
await this.ViewModel.InitializeAsync();
// CollectionViewSource of my GridView being filled
ViewModel.CollectionChanging = true;
GroupInfoCVS.Source = ViewModel.GroupsCollection;
ViewModel.CollectionChanging = false;
// Loading Last selected item - THIS CHANGES SELECTION
ViewModel.LoadLastSelection();
}
After I call the LoadLastSelection method, selection is changed successfuly (I've tested). This is the method that is called after that (in our GridView's extended control):
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.SelectedItemsCount = this.SelectedItems.Count;
var newlySelectedItems = e.AddedItems;
if (newlySelectedItems.Any())
{
var item = newlySelectedItems.Last();
ScrollTo(item);
}
}
private void ScrollTo(object item)
{
UpdateLayout();
var itemUI = (FrameworkElement)this.ContainerFromItem(item);
if (itemUI != null)
{
_scrollViewer.UpdateLayout();
_scrollViewer.ChangeView(null, itemUI.ActualOffset.Y - itemUI.ActualHeight, null, false);
}
}
This also works for the most part. When itemUI is not null, the method scrolls successfully to the required item. The problems start when the items start to overflow the screen size. When items are completely hidden from the screen, they are virtualized. That means that ContainerFromItem returns null, so I can't take the offset properties. Keep in mind that this actually occurs before Library's OnLoaded method is finished.
Please, help me with some alternative to get such properties or other methods of scrolling, which will help me scroll successfully.
I've read a lot and tried using Dispatcher.RunAsync and ScrollIntoView methods, but I couldn't manage to produce any scrolling behavior. If you point me how to use them successfully, that would be a nice help too.
Here's what I've read (and tried):
ItemContainerGenerator.ContainerFromItem() returns null?
How to Know When a FrameworkElement Has Been Totally Rendered?
Is there a "All Children loaded" event in WPF
Let ListView scroll to selected item
Thanks in advance!
IMPORTANT: If you don't want to read all the conversation within the official answer, please read the solution in short here:
TemplatedControl's style had changed ScrollViewer's name from "ScrollViewer" to "LibraryScrollViewer" and that rendered ScrollIntoView method useless.
For GridView, the best way to achieve your needs is to call GridView.ScrollIntoView.
But you seem to have made similar attempts, and it does not to be successful, then the following points may help you:
1. Don't use GridView as a child element of ScrollViewer.
In your code, I see that you are calling the method of ScrollViewer.ChangeView to adjust the view scrolling, so it is speculated that you may put the GridView in the ScrollViewer, which is not recommended.
Because there is a ScrollViewer inside the GridView, and its ScrollIntoView method is to change the scroll area of the internal ScrollViewer. When there is a ScrollViewer outside, the ScrollViewer inside the GridView will lose the scrolling ability, thus making the ScrollIntoView method invalid.
2. Implement the Equals method of the data class.
If your data class is not a simple type (such as String, Int32, etc.), then implementing the Equals method of the data class will help the GridView to find the corresponding item.
Thanks.
Currently my code looks something like this:
private void appbariconNext_Click(object sender, EventArgs e)
{
timerPivot.SelectedIndex += 1;
}
private void appbariconPrevious_Click(object sender, EventArgs e)
{
timerPivot.SelectedIndex -= 1;
}
Problem is, if I press the Previous button from the first PivotItem, I will get an ArgumentException. Same thing if I press the Next button from the last PivotItem.
I currently have a changing number of PivotItem depending on the option chose by users, so it's inefficient for me to get the number of PivotItems from all those different scenarios.
Is there a way to get the total number of PivotItems that a Pivot has? Or is there a way to solve this issue?
Thanks!
First of all, it is bad UI to create buttons to navigate from one page to another using buttons. What about swiping? Have you considered disabling it and restyling Pivot to make it appear as wizard (if that is what you are trying it to be)?
You can get the total number of Pivot items via the Items property. Simply do modular division with that number to keep the index number in range [0, Items.Length>.
I really hope that you are not abusing Pivot in any way.
I am using LongListSelector control on Windows Phone 8 and can't figure out the best way to handle a tap on an item.
The few examples I've found rely on the SelectionChanged event. However, this solution is buggy because if I tap an item that opens a new page, hit back, and then tap the same item again, it won't work because this item is already selected, so SelectionChanged is not triggered.
I tried to register to the tap event and use the current selected item as the tapped one, but some times the current selected item is not the one I expect.
I could wrap my ItemTemplate in a button and handle the tap for each item but I need to reskin the button to make it look like a simple list item.
Finally, I don't understand why it is so complicated to achieve such a basic thing. Is there a simple and standard way I missed?
My second wish is to get an effect on the item when it is tapped. Is there any standard way to do it?
You could null your LongListSelector's SelectedItem at the end of each SelectionChanged event. I.e.
<phone:LongListSelector x:Name="LLS" SelectionChanged="LLS_SelectionChanged">
And the event handler:
private void LLS_SelectionChanged(object sender, SelectionChangedEventArgs e) {
// If selected item is null, do nothing
if (LLS.SelectedItem == null)
return;
// Navigate to the next page
NavigationService.Navigate(new Uri("/nextpage.xaml", UriKind.Relative));
// Reset selected item to null
LLS.SelectedItem = null;
}
You'll fire the SelectionChanged event twice, but nothing's going to happen the second time round and you should get the behaviour that you're looking for - (i.e Setting SelectedItem to null will trigger a new SelectionChanged event, but this second event gets caught in the if-statement)
As for the second part of your question, you might be better posting a new question.
I done it with the Tap event handling.
I prefer not to use Selected property, but get tapped item this way (and I haven't noticed any bugs):
MyListItemClass item = ((FrameworkElement)e.OriginalSource).DataContext
as MyListItemClass;
Also, you could get the original item ContentPresenter simple by navigating up through VisualTree from e.OriginalSource. That way:
ContentPresenter itemPresenter = SomeHelperClass
.FindParent<ContentPresenter>(e.OriginalSource,"");
Where FindParent is similar to find child in this question: How can I find WPF controls by name or type?
ContentPresenter is that object what you need to manually change the item template if you want to (to set "selected" state for example).
private void Item_tap(object sender, RoutedEventArgs e)
{
var element = (FrameworkElement)sender;
DataSource data = (DataSource)element.DataContext;
}
My second wish is to get an effect on the item when it is tapped. Is
there any standard way to do it?
For this the only thing you need to do add this to your control (or stackpanel where you want to have this effect):
<StackPanel toolkit:TiltEffect.IsTiltEnabled="True">
first add this to *.xaml page inside the
LongListSelectorSelectionChanged="listBox_SelectionChanged"
so that it looks like this :
<toolkit:LongListSelector x:Name="listBox" SelectionChanged="listBox_SelectionChanged">
then in the *.xaml.cs file in the event handler
private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Write your logic on what you want to do with the selected item
}
In addition to halilĀ“s answer:
First of all you need to install the Windows Phone Toolkit (WPtoolkit) by NuGet.
Than add the namespace declaration on the PhoneApplicationPage.
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
After this you can add toolkit:TiltEffect.IsTiltEnabled="True" to the control definition.
It is nice documneted by NOKIA:
http://developer.nokia.com/community/wiki/Tilt_Effect_for_Windows_Phone
Oliver
I have a databound listbox on my C# WinForm that holds strings that are links to image file locations. I want to display the image as a thumbnail for the user to click on and view. I got it working correctly by setting DrawMode=OwnerDrawVariable and handling the DrawItem and MeasureItem events.
However I noticed that I have to click exit 2 times to get out of the application (looks like it's called selectedIndexChanged on first click, then exits on second). On further inspection I noticed that the DrawItem event is fired a multitude of times when I click an item in the listbox (like 15+ times). There is only 1-2 items EVER in the listbox at a time! Why is it getting called soo many times?
I tested this with a non databound simple listbox and it does the same thing. I am only curious as I have to read the image from disk and get a thumbnail of it to put into the listbox, which if it did that 15-20 times could affect performance (and is TOTALLY unnecessary).
private void listBox1_MeasureItem(object sender, MeasureItemEventArgs e)
{
MessageBox.Show("listBox1_MeasureItem");
// Cast the sender object back to ListBox type.
ListBox theListBox = (ListBox)sender;
// Get the file path contained in each item.
DataRowView drv = (DataRowView)theListBox.Items[e.Index];
string fileString = drv.Row["fullpath"].ToString();
// Create an image object and load image file into it
Image img = Image.FromFile(fileString);
e.ItemHeight = Convert.ToInt32(img.Height * 0.15);
e.ItemWidth = Convert.ToInt32(img.Width * 0.15);
}
private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
{
MessageBox.Show("listBox1_DrawItem");
// If the item is the selected item, then draw the rectangle
// filled in blue. The item is selected when a bitwise And
// of the State property and the DrawItemState.Selected
// property is true.
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
{
e.Graphics.FillRectangle(Brushes.CornflowerBlue, e.Bounds);
}
else
{
// Otherwise, draw the rectangle filled in beige.
e.Graphics.FillRectangle(Brushes.Beige, e.Bounds);
}
DataRowView drv = (DataRowView)listBox1.Items[e.Index];
Image img = Image.FromFile(drv.Row["fullpath"].ToString());
img = img.GetThumbnailImage(e.Bounds.Width, e.Bounds.Height, null, IntPtr.Zero);
e.Graphics.DrawImage(img, e.Bounds.X, e.Bounds.Y);
// Draw the focus rectangle around the selected item.
e.DrawFocusRectangle();
}
I am not sure and this would have to be tested, but this article makes me believe that what I am going to say is true.
Basically, if you go by the MSDN documentation:
Occurs when a visual aspect of an owner-drawn ListBox changes.
So, this means that everytime an item is added, this event is called. Also, I think even when you perform certain drawing actions within this method, it will call itself (you might be able to avoid this using SuspendLayout and ResumeLayout on the listbox while updating), but am not sure.
Here is the kicker as far as I know it. Everytime this event is triggered, it is pretty much for every item in the list. (This might be useful in that you can de-color a previously selected item, so dont jump directly to what I am about to suggest without thinking it through). So, the DrawItemEventArgs has an index of the item being drawn. Using that, you can focus in on a specific item that you need to draw. That might help you from re-processing something that has already been processed (keep in mind the notes from the above article..copied below, about index being allowed to be -1).
To visualize this process:
Add 1 item ->DrawItem
Add 2nd item->DrawItem
->DrawItem
Add 3rd item->DrawItem
->DrawItem
->DrawItem
Add nth item->DrawItem * n
So, this actually results in a kind of fibonacci type situation (3 items resulted in 6 calls...5 would result in your 15 number), and you can see how the initial load could be cumbersome, and how adding a new item make for n calls to the method.
From the article above:
The ListBox calls the DrawItem method repeatedly, for each item in its
Items collection.
The DrawItemEventArgs argument to the DrawItem event handler exposes
an Index property whose value is the index of the item to be drawn.
Watch out! The system raises the DrawItem event with an index value of
-1 when the Items collection is empty. When that happens you should call the DrawItemEventArgs.DrawBackground() and DrawFocusRectangle()
methods and then exit. The purpose of raising the event is to let the
control draw a focus rectangle so that users can tell it has the
focus, even when there are no items present. The code traps for that
condition, calls the two methods, and then exits the handler
immediately
If a dialog box pops up over the top your control, guess what your control has to do when you close the dialog box? It has to redraw itself!
Try using Debug.Writeline to write debugging information when you are looking at drawing controls. Of course, you still need to be careful that you don't pull up Visual Studio over the top of your form as well! It's a situation where two monitors can really help.
Are there any other methods of bringing a control to the front other than control.BringToFront()?
I have series of labels on a user control and when I try to bring one of them to front it is not working. I have even looped through all the controls and sent them all the back except for the one I am interested in and it doesn't change a thing.
Here is the method where a label is added to the user control
private void AddUserLabel()
{
var field = new UserLabel();
userContainer.Controls.Add(field);
SendLabelsToBack(); // Send All labels to back
userContainer.Controls[field.FieldName].BringToFront();
}
Here is the method that sends all of them to the back.
private void SendLabelsToBack()
{
foreach (var label in userContainer.Controls);
label.SendToBack();
}
Yeah, there's another way. The Controls.SetChildIndex() also changes Z-order. The one with index 0 is the one on top. Doesn't buy you anything though, BringToFront() uses this method.
Your SendLabelsToBack() method as given cannot work, it will also send the label to added to the back. But your next statement fixes that again.
Okay, that doesn't work, which means the BringToFront() method doesn't get executed. Look in the Output window for a "first chance exception" notification. As written, your SendLabelsToBack() will cause an exception if the user control contains any control other than a UserLabel. Also, set a breakpoint after the BringToFront() call and check the value of userContainer.Controls[0].Name when it breaks.
Controls' z-index is per-container.
If you call BringToFront on a control that is inside a container (such as a Panel), it will not bring the container to the front.
Therefore, the control will only go in front of other controls in that container.
To see what containers your controls are in, you can use the Document Outline pane in the View menu.
EDIT: Your userContainer control is probably behind a different control.
Have you tried Invalidate() after BringToFront()? BringToFront does not raise the Paint event
try this:
private void SendLabelsToBack()
{
foreach (var label in userContainer.Controls)
{
label.SendToBack();
label.Invalidate();
}
}
I think you just need to change your last line:
userContainer.Controls[field.FieldName].BringToFront();
to this:
userContainer.Controls[field.Name].BringToFront();
When you use a string as the indexer for the Controls collection, it goes by the Name property of the control (not the FieldName property).
Since you're just trying to bring the most recently-added control to the top, this would also work:
userContainer.Controls[userContainer.Controls.Count - 1].BringToFront();
From my experience looks like windows puts all controls belonging to one graphic container(pane, group box...etc) in a software collection. The collection is ordered by child index which is a property of every control in that container.
The trick is that children with the same index can and do exists. In this case windows will paint those children ordered relative to others but between them it will paint them in the reverse order they had been added to the container.
Long story short: for one container-you need to make sure controls have different indexes by changing ALL NOT just SOME of the indexes when you want to change the z-order. For example:
foreach (Control newControl in TopControl.Controls)
{
TopControl.Controls.SetChildIndex(newControl,indexlogic(newControl));
}
where indexLogic(newControl ) is your method of calculation of the index of particular control.