WPF Bing Maps Control - Change Scale Bar Units - wpf

I'm trying out the Bing Maps control for WPF and this is driving me mad.
I can't figure out how to access the built-in scale bar so I can change the units to metric.
I can add a new scale bar, compass, etc. just fine but can't access the default one.
Anyone have any idea how to do this?

An old question, but there is a much easier way to do this. Just set the Culture property on the map to a country that uses metric.
eg:
<m:Map
x:Name="MapObject"
CredentialsProvider="YOUR_KEY"
Center="-38.1231102,145.2116017"
ZoomLevel="11"
Mode="Aerial"
Culture="en-au"
/>

Here you go buddy. Credit goes to whoever posted the FindVisualChild method on the Internet.
void SomeMethod()
{
var mapScale = FindVisualChild<Scale>(YourBingMapControl);
mapScale.DistanceUnit = DistanceUnit.KilometersMeters;
}
static TChildItem FindVisualChild<TChildItem>(DependencyObject obj) where TChildItem : DependencyObject
{
// Search immediate children first (breadth-first)
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is TChildItem)
return (TChildItem)child;
else
{
var childOfChild = FindVisualChild<TChildItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}

Related

WPF VirtualizingWrapPanel Selected Item Change cause scroll

I have implemented a virtualizing panel that works pretty well, the panel implements IScrollInfo. I am in the process of getting it to work using a keyboard. The Panel is used in a ListView as the ListView.ItemsPanel. When the user presses down the selected item correctly changes but no scrolling occurs and when you reach the last visible item you can not press down any longer. I am trying to figure out how to have the panel aware of the selected item and scroll appropriately. I have tried searching around but I cant seem to find anything related to what I want to do. A point in the right direction would be greatly appreciated.
Edit: Here is the code for the VirtualizingWrapPanel
So I found a solution that works fairly well, I am not sure if it is the best route to take but it seems to work fine.
In the ArrangeOverride method while looping over the children I do the following.
var listViewItem = child as ListViewItem;
if (listViewItem != null)
{
listViewItem.Selected -= ListViewItemSelected;
listViewItem.Selected += ListViewItemSelected;
}
In MeasureOverride I make a call to CleanUpItems in here we will need to unsubscribe from the selected event.
var listViewItem = children[i] as ListViewItem;
if (listViewItem != null)
{
listViewItem.Selected -= ListViewItemSelected;
}
I also added the following three functions.
private void ListViewItemSelected(object sender, RoutedEventArgs e)
{
var listViewItem = sender as ListViewItem;
if(listViewItem == null) return;
var content = listViewItem.Content as CollectionViewGroup;
if(content != null) return; //item is a group header dont click
var items = ItemContainerGenerator as ItemContainerGenerator;
if(items == null) return;
BringIndexIntoView(items.IndexFromContainer(listViewItem));
listViewItem.Focus();
}
protected override void BringIndexIntoView(int index)
{
var offset = GetOffsetForFirstVisibleIndex(index);
SetVerticalOffset(offset.Height);
}
private Size GetOffsetForFirstVisibleIndex(int index)
{
int childrenPerRow = CalculateChildrenPerRow(_extent);
var actualYOffset = ((index / childrenPerRow) * ChildDimension.Height) - ((ViewportHeight - ChildDimension.Height) / 2);
if (actualYOffset < 0)
{
actualYOffset = 0;
}
Size offset = new Size(_offset.X, actualYOffset);
return offset;
}
The GetOffsetForFirstVisibleIndex function will probably vary depending on your implementation but that should be enough info if anyone else is having trouble coming up with a solution.

Outlook-style Spacebar reading with WPF and MVVM

One great feature of Microsoft Outlook is its spacebar reading mode (with the reading pane turned on). Say there are 5 messages in your inbox and the first one is displayed. The displayed message does not entirely fit on the screen, so when you press the spacebar, that is like pagedown within the message. You hit spacebar again, and it pages down again. When you've reached the bottom of the page, and you press spacebar again, it goes to the next message.
What is a good way to do this in WPF (where the application is built using the MVVM pattern)? With MVVM, I use a bunch of DataTemplates instead of usercontrols.
Edit: I should mention that I am using a ListBox for the messages and a FlowDocumentScrollViewer for the message body.
Use Expression Blend's KeyTrigger to invoke the Command in your view model
http://msdn.microsoft.com/en-us/library/microsoft.expression.interactivity.input.keytrigger%28v=expression.40%29.aspx
OR
Use CommandReference from MVVM Toolkit How do I associate a keypress with a DelegateCommand in Composite WPF?
For posterity, here's my solution to the scrolling part of the question. This code handles the space first, then, if the scroll bar is already at the bottom, it doesn't handle the KeyDown. #Hasan's recomended commend fires at that point.
internal class FlowDocumentScrollViewer2 : FlowDocumentScrollViewer
{
private static bool PageDown<T>(T listView)
where T : DependencyObject
{
var scrollViewer = GetVisualChild<ScrollViewer>(listView, null);
var scrollBar = GetVisualChild<ScrollBar>(listView,
bar => bar.Orientation == Orientation.Vertical);
var formerOffset = scrollBar.Track.Value;
scrollViewer.PageDown();
scrollBar.Track.UpdateLayout();
return formerOffset < scrollBar.Track.Value;
}
private static T GetVisualChild<T>(DependencyObject parent, Predicate<T> predicate)
where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual) VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild(v, predicate);
}
if (child != null && (predicate == null || predicate(child)))
{
break;
}
}
return child;
}
public FlowDocumentScrollViewer2()
{
PreviewKeyDown += PreviewSpaceDown;
}
private void PreviewSpaceDown(object sender, KeyEventArgs e)
{
if (e.Handled)
return;
if (e.Key == Key.Space)
{
e.Handled = PageDown(this);
}
}
}

Record items visible to user in ListBox

I have a ListBox or DataGrid filled with thousands of entries. I would like to know items that the user has looked at (scrolling, searching or otherwise). How can I tell what is visible to the user in the ListBox?
Bonus: Set a timer so that the item has to be shown for a minimum of N milliseconds (in the event the user is just pulling down the scrollbar).
Update: This is a near duplicate of Get items in view within a listbox - but the solution it gives, using "SelectedItems", is not sufficient. I need to know the items whether they are selected or not!
All you need to do is to get the underlying StackPanel that's inside the ListBox. It has enough information about which elements are showing. (It implements the interface IScrollInfo).
To get the underlying StackPanel (or actually VirtualizingStackPanel) from a given ListBox, we'll have to use VisualTreeHelper to go through the Visual Tree and look for the VirtualizingStackPanel, like so:
private VirtualizingStackPanel GetInnerStackPanel(FrameworkElement element)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
var child = VisualTreeHelper.GetChild(element, i) as FrameworkElement;
if (child == null) continue;
Debug.WriteLine(child.ToString());
if (child is VirtualizingStackPanel) return child as VirtualizingStackPanel;
var panel = GetInnerStackPanel(child);
if (panel != null)
return panel;
}
return null;
}
Now that we have the StackPanel, we're very close. The StackPanel has the properties VerticalOffset and ViewportHeight (both coming from IScrollInfo) that can give us all the information we need.
private void button1_Click(object sender, RoutedEventArgs e)
{
var theStackPanel = GetInnerStackPanel(MyListBox);
List<FrameworkElement> visibleElements = new List<FrameworkElement>();
for (int i = 0; i < theStackPanel.Children.Count; i++)
{
if (i >= theStackPanel.VerticalOffset && i <= theStackPanel.VerticalOffset + theStackPanel.ViewportHeight)
{
visibleElements.Add(theStackPanel.Children[i] as FrameworkElement);
}
}
MessageBox.Show(visibleElements.Count.ToString());
MessageBox.Show(theStackPanel.VerticalOffset.ToString());
MessageBox.Show((theStackPanel.VerticalOffset + theStackPanel.ViewportHeight).ToString());
}

Restoring exact scroll position of a listbox in Windows Phone 7

I'm working on making an app come back nicely from being tombstoned. The app contains large listboxes, so I'd ideally like to scroll back to wherever the user was while they were scrolling around those listboxes.
It's easy to jump back to a particular SelectedItem - unfortunately for me, my app never needs the user to actually select an item, they're just scrolling through them. What I really want is some sort of MyListbox.ScrollPositionY but it doesn't seem to exist.
Any ideas?
Chris
You need to get hold of the ScrollViewer that is used by the ListBox internally so you can grab the value of the VerticalOffset property and subsequently call the SetVerticalOffset method.
This requires that you reach down from the ListBox through the Visual tree that makes up its internals.
I use this handy extension class which you should add to your project (I've gotta put this up on a blog because I keep repeating it):-
public static class VisualTreeEnumeration
{
public static IEnumerable<DependencyObject> Descendents(this DependencyObject root, int depth)
{
int count = VisualTreeHelper.GetChildrenCount(root);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(root, i);
yield return child;
if (depth > 0)
{
foreach (var descendent in Descendents(child, --depth))
yield return descendent;
}
}
}
public static IEnumerable<DependencyObject> Descendents(this DependencyObject root)
{
return Descendents(root, Int32.MaxValue);
}
public static IEnumerable<DependencyObject> Ancestors(this DependencyObject root)
{
DependencyObject current = VisualTreeHelper.GetParent(root);
while (current != null)
{
yield return current;
current = VisualTreeHelper.GetParent(current);
}
}
}
With this available the ListBox (and all other UIElements for that matter) gets a couple of new extension methods Descendents and Ancestors. We can combine those with Linq to search for stuff. In this case you could use:-
ScrollViewer sv = SomeListBox.Descendents().OfType<ScrollViewer>().FirstOrDefault();

How do I get the Children of a ContentPresenter?

Using the code I can get a content presenter. I would like to locate the first textbox inside it and set the focus accordingly.
Dim obj = TerritoryListViewer.ItemContainerGenerator.ContainerFromItem(myModel)
You can use VisualTreeHelper static class to crawl controls tree.
This is how it can be accomplished in c# (sorry I'm VB dyslexic))
T FindFirstChild<T>(FrameworkElement element) where T: FrameworkElement
{
int childrenCount = VisualTreeHelper.GetChildrenCount(element);
var children = new FrameworkElement[childrenCount];
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(element, i) as FrameworkElement;
children[i] = child;
if (child is T)
return (T)child;
}
for (int i = 0; i < childrenCount; i++)
if (children[i] != null)
{
var subChild = FindFirstChild<T>(children[i]);
if (subChild != null)
return subChild;
}
return null;
}
ContentPresenter has the only child. You get the child simply by
VisualTreeHelper.GetChild(yourContentPresenterObj, 0);
If you need to go deeper - down to a first found TextBox, then, yes, you use the more comprehensive approach suggested by #alpha-mouse.
Dim myContentPresenter = CType(obj, ContentPresenter)
Dim myDataTemplate = myContentPresenter.ContentTemplate
Dim target = CType(myDataTemplate.FindName("txtQuantity", myContentPresenter), TextBox)
In my case I needed to iterate on all controls of a certain base type placed on a custom canvas which was being used inside an ItemsControl.
This Linq expression was used to get those controls from within MeasureOverride():
var foobarControls =
InternalChildren
.OfType<ContentPresenter>()
.Where(c => VisualTreeHelper.GetChildrenCount(c) > 0)
.Select(c => VisualTreeHelper.GetChild(c, 0))
.OfType<FoobarControlBase>();
This guards against cases where the ContentPresenter had no children. I found in some instances depending on when this was called the visual tree might not be established and as a result the ContentPresenters would have no children. (This situation might have been a bug in itself, actually, but nonetheless this code turned out to be reliable.)

Resources