WP7: Get current PivotItem for data-bound Pivot - windows-phone-7

I have a Pivot, whose ItemsSource is set to a collection of data objects, and I use an ItemTemplate to transform the items into UI content (I also use a HeaderTemplate).
When tombstoning, I normally get the ScrollViewer from the current PivotItem and save the current position away, so that I can scroll back to the right position if the user navigates back to my app. This works fine if I hard-code the PivotItems in my XAML.
My issue is that the when the Pivot is bound to my data object collection using ItemsSource, SelectedItem returns one of my data objects - not the PivotItem. I can't see how to get to the current PivotItem (or the UI elements generated from my ItemTemplate). I've noticed protected members to go from a ItemsSource item to its corresponding container - perhaps I need to derive from Pivot to make use of these?
Thanks!
Damian

Another way to get the Pivot item is described here - http://bea.stollnitz.com/blog/?p=7
Get a referenec to the Pivot control and you can then use Pivot.ItemContainerGenerator.ContainerFromIndex(index) tp get the PivotItem or you can use Pivot.ItemContainerGenerator.ContainerFromItem(dataobject)

I've upvoted Derek's answer since it sent me in the right direction. The extension method he suggested only went one level deep though, so I've come up with the following recursive extension method which works for me:
internal static T FindVisualChild<T>(this DependencyObject parent,
Func<T, bool> filter)
where T : DependencyObject
{
var childCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < childCount; i++)
{
var elt = VisualTreeHelper.GetChild(parent, i);
if (elt is T && filter((T)elt)) return (T)elt;
var result = FindVisualChild(elt, filter);
if (result != null) return result;
}
return null;
}
And then I use it as follows:
var item = pivot.FindVisualChild<PivotItem>(
elt => elt.DataContext == pivot.SelectedItem);
This approach is just a refinement of Derek's -- all kudos to him.

If, for some reason, you need the actual current PivotItem and not the data that is associated with it, then I think you'll need to traverse the visual tree to find it. I use the following methods to help in this:
/// <summary>
/// Gets the visual children of type T.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="target"></param>
/// <returns></returns>
public static IEnumerable<T> GetVisualChildren<T>(this DependencyObject target)
where T : DependencyObject
{
return GetVisualChildren(target).Where(child => child is T).Cast<T>();
}
/// <summary>
/// Get the visual tree children of an element.
/// </summary>
/// <param name="element">The element.</param>
/// <returns>The visual tree children of an element.</returns>
/// <exception cref="T:System.ArgumentNullException">
/// <paramref name="element"/> is null.
/// </exception>
public static IEnumerable<DependencyObject> GetVisualChildren(this DependencyObject element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return GetVisualChildrenAndSelfIterator(element).Skip(1);
}
/// <summary>
/// Get the visual tree children of an element and the element itself.
/// </summary>
/// <param name="element">The element.</param>
/// <returns>
/// The visual tree children of an element and the element itself.
/// </returns>
private static IEnumerable<DependencyObject> GetVisualChildrenAndSelfIterator(this DependencyObject element)
{
Debug.Assert(element != null, "element should not be null!");
yield return element;
int count = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < count; i++)
{
yield return VisualTreeHelper.GetChild(element, i);
}
}
So you would use this as follows:
var selectedPivotItem = this._pivot
.GetVisualChildren()
.Select(p => p.DataContext == this._pivot.SelectedItem)
.FirstOrDefault();

Why not use the SelectedIndex property?

Related

Changes to Pushpin.Location Inside ObservableCollection Don't Affect The Pushpin Until Map Is Redrawn

First question here, so far I've received great help just reading the answers. This is something I couldn't find any answers on though. So here comes...
We have a Bing Maps map whose MapItemsControl is bound to ObservableCollection<Pushpin> Property. When adding/removing items to the collection the map gets updated correctly.
Now my question is this: how to update/bind the location of a Pushpin inside the collection so it is reflected on the map without redrawing the map by moving/zooming?
Here is the Map.xaml:
<phone:PhoneApplication ...
DataContext="{Binding Source={StaticResource Locator}, Path=Main}">
<maps:Map ...>
<maps:MapItemsControl ItemsSource="{Binding MapItems}"/>
</maps:Map>
</phone:PhoneApplication>
MainViewModel.xaml:
#region MapItems
#region MapItems Property
/// <summary>
/// The <see cref="MapItems" /> property's name.
/// </summary>
public const string MapItemsPropertyName = "MapItems";
private ObservableCollection<Pushpin> _MapItems =
new ObservableCollection<Pushpin>();
/// <summary>
/// Sets and gets the MapItems property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public ObservableCollection<Pushpin> MapItems
{
get
{
return _MapItems;
}
set
{
if (_MapItems == value)
{
return;
}
_MapItems = value;
RaisePropertyChanged(MapItemsPropertyName);
}
}
#endregion
#region OwnLocation
private Pushpin OwnLocation;
private void InitializeOwnLocation()
{
OwnLocation = new Pushpin()
{
Style = App.Current.Resources["OwnLocationStyle"] as Style
};
Binding b = new Binding {
Path = new PropertyPath("LastKnownLocation")
};
OwnLocation.SetBinding(Pushpin.LocationDependencyProperty, b);
MapItems.Add(OwnLocation);
}
#endregion
...
#endregion
LastKnownLocation is being set in the PositionChanged of the GeoCoordinateWatcher
Update (30.5.2012 20.35). Implementation of LastKnownLocation property.
/// <summary>
/// The <see cref="LastKnownLocation" /> property's name.
/// </summary>
public const string LastKnownLocationPropertyName = "LastKnownLocation";
private GeoCoordinate _LastKnownLocation;
/// <summary>
/// Sets and gets the LastKnownLocation property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public GeoCoordinate LastKnownLocation
{
get
{
return _LastKnownLocation;
}
private set
{
if (_LastKnownLocation == value)
{
return;
}
var oldValue = _LastKnownLocation;
_LastKnownLocation = value;
Settings["LastKnownLocation"] = _LastKnownLocation;
RaisePropertyChanged(LastKnownLocationPropertyName, oldValue, value, true);
}
}
x:Name= myMap on map Control
When you want to rebind your map pins
set
myMap.Children.Clear()
then re-add your Pins.
you can notify your data change
As I commented earlier after long Googling I finally came to a workaround that doesn't seem to be available anymore. The trick is to call SetView on the map view after changing Pushpin.Location.

Bind ListBox (or Telerik RadListPicker) to Enum

I have an enum:
public enum UnitOfMeasure
{
Meters,
Kilometers,
Yards,
Miles,
Time
}
and I want to bind it to a ListBox (actually a Telerik RadListPicker, but it works the same):
<telerikInput:RadListPicker
Header="Measure work in:"
ItemsSource="{Binding WorkUnitOfMeasure}"
HeaderStyle="{StaticResource HeaderStyle}"
x:Name="workUnitsListPicker"
Margin="18">
</telerikInput:RadListPicker>
My View Model:
/// <summary>
/// The <see cref="WorkUnitOfMeasure" /> property's name.
/// </summary>
public const string WorkUnitOfMeasurePropertyName = "WorkUnitOfMeasure";
private ObservableCollection<Enum<UnitOfMeasure>> _workUnitOfMeasure;
/// <summary>
/// Gets the WorkUnitOfMeasure property.
/// Changes to that property's value raise the PropertyChanged event.
/// This property's value is broadcasted by the Messenger's default instance when it changes.
/// </summary>
public ObservableCollection<Enum<UnitOfMeasure>> WorkUnitOfMeasure
{
get
{
return _workUnitOfMeasure;
}
set
{
if (_workUnitOfMeasure == value)
{
return;
}
var oldValue = _workUnitOfMeasure;
_workUnitOfMeasure = value;
RaisePropertyChanged(WorkUnitOfMeasurePropertyName);
}
}
And in my constructor I have tried variations of something like this:
WorkUnitOfMeasure = new ObservableCollection<Enum<UnitOfMeasure>>();
I can't seem to get the listbox to bind to Enum. I know I'm missing something simple, but I can't figure it out.
I believe it should be an
ObservableCollection<UnitOfMeasure>
not
ObservableCollection<Enum<UnitOfMeasure>
If not what error are you seeing?
If you are binding the single enum items to listbox:
Following code may help you:
List<string> newList = new List<string>();
for (int i = 0; i <= (int)UnitOfMeasure.Time; i++)
newList.Add(Enum.GetName(typeof(UnitOfMeasure), i));
SampleList.ItemsSource = newList;

'Expected class, delegate, enum, interface,or struct' in XNA 4.0?

Here's my code;
I've looked it up online, but still unable to fix it.
Please show me a fixed version of the code, thank you so much! I've been staring at the screen for half 'n' hour and still can't figure this out!
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D car1Texture;
Vector2 car1Position = new Vector2(200f, 100f);
Texture2D background;
Rectangle mainFrame;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
//Change the resolution to 800x600
graphics.PreferredBackBufferWidth = 1000;
graphics.PreferredBackBufferHeight = 800;
graphics.ApplyChanges();
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// Load the background content.
background = Content.Load<Texture2D>("roadscan");
// Set the rectangle parameters
mainFrame = new Rectangle(0, 0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height);
spriteBatch = new SpriteBatch(GraphicsDevice);
car1Texture = Content.Load<Texture2D>("car1");
}
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
KeyboardState keyboard = Keyboard.GetState();
GamePadState gamePad = GamePad.GetState(PlayerIndex.One);
if (keyboard.IsKeyDown(Keys.Left) || gamePad.DPad.Left == ButtonState.Pressed)
{
ballPosition.X -= 3f;
}
if (keyboard.IsKeyDown(Keys.Right) || gamePad.DPad.Right == ButtonState.Pressed)
{
ballPosition.X += 3f;
}
if (keyboard.IsKeyDown(Keys.Up) || gamePad.DPad.Right == ButtonState.Pressed)
{
ballPosition.Y += 3f;
}
if (keyboard.IsKeyDown(Keys.Down) || gamePad.DPad.Right == ButtonState.Pressed)
{
ballPosition.Y -= 3f;
}
// TODO: Add your update logic here
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// Draw the background.
// Start building the sprite.
spriteBatch.Begin();
// Draw the background.
spriteBatch.Draw(background, mainFrame, Color.White);
// End building the sprite.
spriteBatch.End();
// TODO: Add your drawing code here
base.Draw(gameTime);
}
}
Remove the extra brace above the UnloadContent method
Right here:
// TODO: use this.Content to load your game content here
}<-------Delete this guy!!!
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
You've got one to many closing curly braces.
protected override void LoadContent()
{
// snip
} //close method
// TODO: use this.Content to load your game content here
} //close class
You accidentally close the class half way through, just removing the curly brace will fix things. This is common when you haven't shrunk or removed the default comment blocks as they tend to get in the way after you've learnt what all the basic methods do.

Setting the scrollviewer vertical offset when the scrollviewer is inside a datatemplate (wp7)

I have a pivot where each pivotItem contains a scrollviewer.
What I want to do is to set the scrollviewer's offset to a specific number each time I scroll to a new pivot item. I cannot create a databinding because the offset value is not exposed.
There is a ScrollToVerticalOffset() that i can call, but I need first to find which scrollviewer is currently active and get that object, which means the scrollviewer inside the currently selected pivot item.
I tried to get the scrollviewer by traversing the visual tree based on its name but I always get the 1st scrollviewer.
How could I do that?
thanx
You could traverse the visual tree by type instead of by name and start at the selected PivotItem, which should mean that the first ScrollViewer that you find will be the one you want.
/// <summary>
/// Gets the visual children of type T.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="target"></param>
/// <returns></returns>
public static IEnumerable<T> GetVisualChildren<T>(this DependencyObject target)
where T : DependencyObject
{
return GetVisualChildren(target).Where(child => child is T).Cast<T>();
}
/// <summary>
/// Get the visual tree children of an element.
/// </summary>
/// <param name="element">The element.</param>
/// <returns>The visual tree children of an element.</returns>
/// <exception cref="T:System.ArgumentNullException">
/// <paramref name="element"/> is null.
/// </exception>
public static IEnumerable<DependencyObject> GetVisualChildren(this DependencyObject element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return GetVisualChildrenAndSelfIterator(element).Skip(1);
}
/// <summary>
/// Get the visual tree children of an element and the element itself.
/// </summary>
/// <param name="element">The element.</param>
/// <returns>
/// The visual tree children of an element and the element itself.
/// </returns>
private static IEnumerable<DependencyObject> GetVisualChildrenAndSelfIterator(this DependencyObject element)
{
Debug.Assert(element != null, "element should not be null!");
yield return element;
int count = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < count; i++)
{
yield return VisualTreeHelper.GetChild(element, i);
}
}
So you'd end up with something like this:
var scroller = ((PivotItem)pivot.SelectedItem).GetVisualChildren().FirstOrDefault();
scroller.ScrollToVerticalOffset(offset);

Watin AddDialogHandler method throwing null reference exception in FF

I'm trying to add ConfirmDialogHandler to dialog handler and i get NullReferenceException.
This happens on FF 3.6 with latest watin version, the same code works on IE7 and 8.
ConfirmDialogHandler confirmDialogHandler = new ConfirmDialogHandler();
browser.AddDialogHandler(confirmDialogHandler); // --> exception here
Any ideas?
Old Solution
Found the problem.
The DialogWatcher was not initialized when FF session was created added this line to watin code:
private void CreateFireFoxInstance(string url)
{
Logger.LogAction("Creating FireFox instance");
UtilityClass.MoveMousePoinerToTopLeft(Settings.AutoMoveMousePointerToTopLeft);
var clientPort = GetClientPort();
clientPort.Connect(url);
_ffBrowser = new FFBrowser(clientPort);
StartDialogWatcher(); // <-- Added line
WaitForComplete();
}
Please Note - This is the workaround
The provided solution didn't worked
This is my workaround:
I used AutoIt dll and handled the popups by my self, code sample:
using AutoItX3Lib;
public static void RunAutomaticEnterCommand(Session session)
{
var autoIt = GetPopupHandle();
autoIt.Send("{ENTER}");
Thread.Sleep(1000);
}
/// <summary>
/// Cancel the popup
/// For FF
/// </summary>
/// <param name="session"></param>
public static void RunAutomaticCancelCommand(Session session)
{
var autoIt = GetPopupHandle();
autoIt.Send("{TAB}");
autoIt.Send("{ENTER}");
Thread.Sleep(1000);
}
/// <summary>
/// Return the autoit popup handler
/// AutoIt is a script language, we using it to handle the firefox popups
/// </summary>
/// <returns></returns>
private static AutoItX3Class GetPopupHandle()
{
var autoIt = new AutoItX3Class();
autoIt.AutoItSetOption("WinTitleMatchMode", 2);
const string partialTitle = "The page at"; //the popup in Firefox starts with this text
autoIt.WinWait(partialTitle, "", 30);
string fullTitle = autoIt.WinGetTitle(partialTitle); //Get the whole popup title
autoIt.WinActivate(fullTitle); //Get focis to the popup
if (autoIt.WinWaitActive(fullTitle, "", 20) == 0)
{
reporter.Report("Failed to get the FireFox popup handle", false);
return autoIt;
}
return autoIt;
}

Resources