Maps elements display as text (class name) - xaml

I use WinRt MapControl on Windows Phone 8.1. But when I trying add MapIcon or MapPolyline map elements I get only text like this:
XAML code looks like this:
<maps:MapControl x:Name="MapOnScreenControl"
MapServiceToken="12345">
<maps:MapPolyline Path="{Binding Route, Converter={StaticResource RouteToGeopathConverter}}"/>
</maps:MapControl>
What I am doing is wrong? Thanks.

Unfortunately map elements cannot be added to the map via XAML. You will need to add them within code.
MapOnScreenControl.MapElements.Add(new MapPolyline());
A trick I like to do is use the viewmodel to add elements to the map by either setting a Map property or a MapElements property of my viewmodel.
private void MapPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
var vm = DataContext as MapViewModel;
vm.MapElements = MyMap.MapElements;
}
Then within the viewmodel you can add items to the elements.
You can also add a collection of items using the MapItemsControl.
<maps:MapControl x:Name="Map" MapServiceToken="abcdef-abcdefghijklmno">
<maps:MapItemsControl ItemsSource="{Binding Locations}">
<maps:MapItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="Assets/Mappin.png" Height="25"
maps:MapControl.NormalizedAnchorPoint="1,0.5"
maps:MapControl.Location="{Binding Geopoint}" />
</DataTemplate>
</maps:MapItemsControl.ItemTemplate>
</maps:MapItemsControl>
</maps:MapControl>

Related

Dynamically change DataTemplate for a ListView at Runtime

I have 2 DataTemplates for displaying the contents of ClassA or ClassB inside a single ListView; which template to select will be based on a RadioButton selection by the user.
Is it possible to change the ItemTemplate of a ListView (in XAML) based on user input dynamically at runtime?
An example snippet of code:
XAML Page:
<Page...>
<Page.Resources>
<DataTemplate x:Key="ClassAListViewItemTemplate" x:DataType="vm:ClassA" ... />
<DataTemplate x:Key="ClassBListViewItemTemplate" x:DataType="vm:ClassB" ... />
</Page.Resources>
<RelativePanel>
<RadioButton Content="ClassA" ... />
<RadioButton Content="ClassB" ... />
<ListView DataContext="{Binding Path=MainViewModel}"
ItemsSource="{Binding ListOfClassAOrB, Mode=TwoWay}"
ItemTemplate="{StaticResource ClassAListViewItemTemplate}"/>
</RelativePanel>
</Page>
I have stripped the code down somewhat to the essentials, but I would like to be able to change the following at runtime:
ItemTemplate="{StaticResource ClassAListViewItemTemplate}"
I have seen solutions for Classic WPF applications that use Style.Triggers, but these aren't applicable for UWP
Marco Minerva's blog on Adaptive Triggers, RelativePanel and DataTemplate in the Universal Windows Platform talks of using UserControls within DataTemplates to modify the visual state using Adaptive Triggers, but this doesn't take into account switching out of templates based on user input
The closest answer I have found to my problem is another blog he wrote "Dynamically choose DataTemplate in WinRT" where there is an element of code-behind involved - but it only appears to be an if statement - but its the cleanest solution I have come across thus far, and what I'd like to replicate in XAML
Thanks
you need to use overwrite SelectTemplateCore of Data template. Change your view model like this.
Below code will helps you.
public class SampleViewModel : DataTemplateSelector
{
public DataTemplate ClassAListViewItemTemplate{ get; set; }
public DataTemplate ClassBListViewItemTemplate{ get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
var itemsData = item as SampleClass; // add your Data class
if (itemsData.IsAddButton == false) // define any property to select the datatemplate
{
return ClassAListViewItemTemplate;
}
else
{
return ClassBListViewItemTemplate;
}
}
}
Add your two datatemplates to one key, and give the key to ItemTemplateSelector property in gridview.
<viewModels:SampleViewModel x:Key="FeedbackTempateSelector"
ClassAListViewItemTemplate="{StaticResource ClassAListViewItemTemplate}"
ClassBListViewItemTemplate="{StaticResource ClassBListViewItemTemplate}">
</viewModels:SampleViewModel>

Using x:Bind inside the GridView's ItemTemplate layout User Control in UWP

In the Universal Windows Platform API, how do I use x:Bind inside of a User Control (intended to be the layout for a GridView's ItemTemplate) to bind to instance properties of a GridView's ItemSource?
Background
I'm trying to re-create the layout found in Windows 10 stock apps like Sports, News, Money, etc.
I'm using a two GridViews for the main area of the app; one for "featured articles" (2 large photos w/ headlines) and one for all the other articles (smaller photos w/ headlines).
I'm able to bind to a data source that I supply in the code behind (a List where NewsItem is a POCO with a Image and Headline property) Here's the pertinent parts of the MainPage.xaml:
<Page ...
xmlns:data="using:NewsApp.Models" />
....
<GridView Name="FeaturedItems" Grid.Row="0">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:NewsItem">
<Grid Name="mainPanel" HorizontalAlignment="Stretch" Width="500" >
<Image Source="{x:Bind Image}" HorizontalAlignment="Stretch" />
<TextBlock Text="{x:Bind Headline}" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
....
The Image and Headline bind just fine (even though they've not been styled correctly). However, instead I think I need to bind to a User Control to get the styling options I want, control over resizing esp. when using Visual State Triggers and to simplify the XAML in general (at least, this was the technique suggested to me.)
So, I added a new User Control to the project (FeaturedItemControl.xaml), and copied in the DataTemplate's child Grid:
<UserControl ... >
<Grid Name="mainPanel" HorizontalAlignment="Stretch" Width="500" >
<Image Source="{x:Bind Image}" HorizontalAlignment="Stretch" />
<TextBlock Text="{x:Bind Headline}" />
</Grid>
</UserControl>
And then back in the MainPage.xaml, I change the DataTemplate to reference the new FeaturedItemControl:
<GridView Name="FeaturedItems" Grid.Row="0">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:NewsItem">
<local:FeaturedItemControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
However, I get the error message for both Image and Headline properties: Invalid binding path 'Headline': Property 'Headline' can't be found on type 'FeaturedItemControl'.
I've tried a few things but am flailing just throwing code at the problem without understanding what I'm doing. Any help would be greatly appreciated.
Thank you for your kind attention.
Using Depechie's answer, I formulated this little cheat cheat for posterity:
Do note that you MUST use this technique to utilize the VisualStateManager with items inside your data bound controls' (GridView, ListView) data templates.
1) Create a User Control.
2) Cut the content of the DataTemplate in your page and paste it into the User Control replacing the template's Grid.
3) Reference the User Control from inside the Data Template:
4) Modify the contents of the User Control changing x:Bind statements to utilize object.property notation:
<UserControl>
<StackPanel>
<Image Source="{x:Bind NewsItem.LeadPhoto}" />
<TextBlock Text="{x:Bind NewsItem.Headline}" />
<TextBlock Text="{x:Bind NewsItem.Subhead}" />
</StackPanel>
</UserControl>
5) Add this in the User Control's Code Behind:
public Models.NewsItem NewsItem { get { return this.DataContext as Models.NewsItem; } }
public ContactTemplate()
{
this.InitializeComponent();
this.DataContextChanged += (s, e) => Bindings.Update();
}
Well it's possible to use x:Bind in user controls, but you'll need to add some extra code behind.
I encountered the same problem in my project, you can see the result here : https://github.com/AppCreativity/Kliva/tree/master/src/Kliva/Controls
So what you need to do is, create a property in the code behind of your user control that points to the correct DataContext.
If you do that, you can use properties of that DataContext in the xaml of your control: for example:
Do note that in the constructor of your control you do need to add: DataContextChanged += (sender, args) => this.Bindings.Update(); because the datacontext will change depending on the page where your control is used!
Then on the page where you are placing this control, you'll also need to do the same to enable the x:bind to work.
You'll see this in my example on the MainPage.DeviceFamily-Mobile.xaml and MainPage.xaml.cs files.
Hope this helps.
x:Bind isn't really hierarchical like Binding/DataContext is. Additionally when you're not directly inside a DataTemplate (such as inside your user control) the object that x:Bind tries to use is 'this' rather than 'this.DataContext'. My current line of thinking on how to solve this sort of issue is to try not to use UserControls anywhere. Instead preferring DataTemplates contained within a ResourceDictionary. There are some pretty strong caveats to this approach though, you will for example crash the xaml compiler if you use x:Bind inside a data template that was created from the ResourceDictionary item template (add new item). you can find a pretty complete example here https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/XamlBind its important to note in the sample where they show the ResourceDictionary being used that its not actually just a ResourceDictionary.xaml its also a ResourceDictionary.xaml.cs (this is where the generated code from x:Bind ends up)
Another option is to add Headline and Image as properties on your user control and x:Bind them from the template, then inside the user control x:Bind as you are currently doing, but now the x:Bind generated path 'this.Headline' will exist. Unfortunately the order things are actually bound means that the x:Bind's you have inside your user control will have to be OneWay rather than the default OneTime. this is because x:Bind OneTime does the bind inside the InitializeComponent call, and any set of properties/DataContext stuff doesn't get done until after that has already run.
So to sum this up, you have two options, use data templates everywhere, or bind to properties that are directly on the user control.

Numbering a ListView

I have a ListView whose Data Context is an ObservableCollection. I then used XAML to format the items and bind to their properties in the ListView in ListView.ItemTemplate.DataTemplate . Now, I want to add a TextBlock here to display the position of the item in the ListView through Binding in XAML. How to do that?
If I got your question right, you just need to add a TextBlock in each item DataTemplate with the index number inside the ListView, right?
If so, you can easily do that by doing something like this:
<DataTemplate x:Key="ItemTemplate">
<Grid>
<!--All your various UI elements here...-->
<!--Add a TextBlock with the formatting you want-->
<TextBlock Text="{Binding Position}"/>
</Grid>
</DataTemplate>
And in C#, add something like this after you've created your Source ObservableCollection, before assigning it to the Property inside your ViewModel:
for (int i = 0; i < myCollection.Count; i++)
{
myCollection[i].Position = Convert.ToString(i);
}
//And then, the usual
Source = myCollection
NB: I'm assuming you have a Property called Source in your ViewModel with INotifyPropertyChanged implemented. You will also have to modify the class you use inside your ObservableCollection and add the Position property of course :)

Windows Phone 8.1 Toggling the visibility of a TextBlock in a DataTemplate

I'm building a Windows Phone 8.1 Hub Application. One of the hub section contains a ListView that displays a list of articles. I'd like to add a Textblock to this hubsection which displays a message when the articles failed to download. The XAML Code is below:
<HubSection
x:Uid="ArticlesSection"
Header="ARTICLES"
DataContext="{Binding Articles}"
HeaderTemplate="{ThemeResource HubSectionHeaderTemplate}">
<DataTemplate>
<Grid>
<ListView
AutomationProperties.AutomationId="ItemListViewSection3"
AutomationProperties.Name="Items In Group"
SelectionMode="None"
IsItemClickEnabled="True"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource BannerBackgroundArticleTemplate}"
ItemClick="ItemView_ItemClick"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
</ListView>
<TextBlock
x:Name="NoArticlesTextBlock"
HorizontalAlignment="Center"
VerticalAlignment="center"
Style="{StaticResource HeaderTextBlockStyle}"
TextWrapping="WrapWholeWords"
TextAlignment="Center"/>
</Grid>
</DataTemplate>
</HubSection>
The problem I'm having is that I can't access the TextBlock from the C# code. Is there an easier way to do this?
The problem I'm having is that I can't access the TextBlock from the C# code.
Yes, since the TextBlock is defined inside a DataTemplate, the TextBlock won't be available until the DataTemplate has been applied. Thus, the x:Name attribute won't automatically generate a variable reference in the InitializeComponent method in your *.g.i.cs file. (Read up on XAML Namescopes for more information).
If you want to access it from your code-behind, there are two ways:
The first way is the simplest: you can get a reference to the TextBlock in the sender argument of the Loaded event handler for that TextBlock.
<TextBlock Loaded="NoArticlesTextBlock_Loaded" />
Then in your code-behind:
private TextBlock NoArticlesTextBlock;
private void NoArticlesTextBlock_Loaded(object sender, RoutedEventArgs e)
{
NoArticlesTextBlock = (TextBlock)sender;
}
The second way is to traverse the visual tree manually to locate the element with the required name. This is more suitable for dynamic layouts, or when you have a lot of controls you want to reference that doing the previous way would be too messy. You can achieve it like this:
<Page Loaded="Page_Loaded" ... />
Then in your code-behind:
static DependencyObject FindChildByName(DependencyObject from, string name)
{
int count = VisualTreeHelper.GetChildrenCount(from);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(from, i);
if (child is FrameworkElement && ((FrameworkElement)child).Name == name)
return child;
var result = FindChildByName(child, name);
if (result != null)
return result;
}
return null;
}
private TextBlock NoArticlesTextBlock;
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// Note: No need to start searching from the root (this), we can just start
// from the relevant HubSection or whatever. Make sure your TextBlock has
// x:Name="NoArticlesTextBlock" attribute in the XAML.
NoArticlesTextBlock = (TextBlock)FindChildByName(this, "NoArticlesTextBlock");
}
Jerry Nixon has a good page on his blog about this.

Silverlight Find Control in Data Template

I have the following xaml code:
<dxb:BarButtonItem Name="btnPrev" Cursor="Hand" ItemClick="btnPrev_ItemClick">
<dxb:BarButtonItem.ContentTemplate>
<DataTemplate>
<Image x:Name="imgSkipLeft" Source="/ProjectTool;component/images/arrowleft.png" Height="16" Width="16">
<ToolTipService.ToolTip>
<TextBlock x:Name="txtBlockTip_Left"/>
</ToolTipService.ToolTip>
</Image>
</DataTemplate>
</dxb:BarButtonItem.ContentTemplate>
</dxb:BarButtonItem>
How can I find txtBlockTip_Left and modify the text
Rather that trying to find the TextBlock on the DataTemplate to change the text it would be better to set the text through a binding and then change the property that the Text property is bound to. The following resource has an example on how to use data binding within a DataTemplate: http://www.silverlight.net/learn/data-networking/binding/data-binding-to-controls-%28silverlight-quickstart%29
Using the VisualTreeHelperExtensions class and put a grid inside the data template and give it a name, in the example below the name is grdTemplate.
var bttn= btnPrev.ItemContainerGenerator.ContainerFromItem(btnPrev);
var dataTemplate = bttn.GetDescendantsOfType<Grid>().FirstOrDefault(g => g.Name == ("grdTemplate"));
var textBlocks= VisualTreeHelperExtensions.GetDescendantsOfType<TextBlock>(dataTemplate);
TextBlock txtBlockTip_left = textBlocks.ElementAt(0);