How do I refer to simple strings bound to a Silverlight Toolkit ListPicker in a DataTemplate? - xaml

I am styling the ListPicker with a DataTemplate. All the examples I have read assume that the ListItems are bound complex objects and so can refer to properties on the objects using the usual Binding Property syntax. e.g.
this.myListPicker.Items.Add(new Profile() { Name = "Joe",
Occupation="Button pusher" });
and in the XAML,
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding Name}" />
<TextBox Text="{Binding Occupation}" />
</StackPanel>
</DataTemplate>
However my ListPickers are bound to a List of simple strings and there does not seem to be a property on the string that actually refers to the string.
How do I get a handle to the string inside the DataTemplate so that I can assign them to a e.g. TextBox in the template?

In the example code you show, you do actually have a "complex" object, a Profile, and the binding you specify should work.
If you really just have strings as the items, then the binding in your ItemTemplate would look like:
<TextBox Text="{Binding }" />
The relevant detail is that all ItemsControls (and ListPicker seems to be one) set the DataContext of the view of each item to the item. The binding syntax, above, means "bind to the source itself", and, unless otherwise specified (like with Source or RelativeSource, etc.), the source of any binding is the DataContext.
MSDN has more details here.

Related

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.

ContentControl does not change the content - function never called

I want to change my content off an AppBar dynamicly whith this code:
<Page.Resources>
<local:AppBarSelector x:Key="myAppBarSelector"/>
</Page.Resources>
<Page.BottomAppBar>
<AppBar>
<ContentControl Content="{Binding SelectedItem, ElementName=listBox}" ContentTemplateSelector="{StaticResource myAppBarSelector}">
<ContentControl.Resources>
<DataTemplate x:Key="1">
<TextBlock Text="Hallo Welt 1" Foreground="White" />
</DataTemplate>
<DataTemplate x:Key="2">
<TextBlock Text="Hallo Welt 2" Foreground="White" />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</AppBar>
</Page.BottomAppBar>
And this is my Code behind:
public class AppBarSelector : DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
Debug.WriteLine((string)item);
if (item == null) return base.SelectTemplateCore(item, container);
var contentControl = (ContentControl)container;
var templateKey = (string)item;
return (DataTemplate)contentControl.Resources[templateKey];
}
}
But this method is nerver called.Even the Debug.WriteLine function. Where is my mistake?
Just after some comments here...
(note: this is a bit general but I can't be more specific w/o some more code to reflect the issues)
This should work 'as is' - I don't see any problems that would produce that (I check with similar example fast and it works well with .ItemsSource = new List<string>{...}.
So that's not the culprit - but it doesn't hurt what I suggested - make a proper MVVM binding to properties, make the list ObservableCollection<> - and also it's always recommended to have a more higher-level objects (instead of just string) as your items (helps in many cases with binding with similar issues - that object implements INotifyPropertyChanged etc. - and you bind to a 'property' there, not the entire object).
The other error suggests some issues as well.
And lastly to bind two contentControls together - you don't normally need events as such. You can use Triggers from the style or XAML directly - but most of the time just bind both to a property in the view-model - and handle the 'change' in your property 'setter'.
You should put up a small primer that repeats this - who knows it might help you realize what you're doing wrong.

App crashing when assigning button-name through binding

I have buttons in a listbox and for each of them, I want
to provide a different name. This is how I setup the binding in XAML:
<Button Content="{Binding count}"
x:Name="{Binding buttonName}"
Width="55"
Height="55"
BorderThickness="3"
FontSize="18.667"
Padding="-1,-2,0,0"
Margin="-400,0,0,0"
FontWeight="Bold"
Click="servingButtonClicked" />
buttonName is a string, which is stored in an object in a LinkedList<string> as an itemSource for this listBox.
Why won't this work?
Error:
Object reference not set to an instance of an object.
x:Name must not bet via DataBinding - this is a name for the instance of the object, not something dynamic.

Access Bound Data in ListBox.ItemTemplate using Custom Control (Silverlight 4)

I Have a ListBox and I am defining its ItemsTemplate using a custom control. I want the control to be displayed in different ways depending on the state of cetain properties of the object. How can I access the item that the ListBox.ItemTemplate is bound to?
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<custom:MyControl />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
With in the code of the custom control how would I do something like:
if((this.DataContext as SomeObject).CollectionProperty.Count() > 0)
DoAction();
I was accessing dataContext in the constructor and it was null. I accessed it in the this.Loaded event and it worked fine.

Binding to "this" for itemsource?

Say I have a listbox, whose itemsource is bound to a Collection of classes Foo
How can I xaml bind a property directly to the instance of Foo?
The property I want to bind is the source of an Image, and the Foo class has several properties that influence the source, so in my Converter class. I want Foo
If you mean that you have the ItemsSource property of an ListBox bound to the collection (you don't bind to controls, but to their properties), and you want to show an image for each Foo in the collection, you can do it like this:
<ListBox ItemsSource="{Binding whatever}">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Converter={StaticResource converter}}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Just by leaving the binding's path empty, it is set to the current item.