UWP XAML Intellisense DataTemplate.DataType - xaml

Why does intellisense filter out interfaces and abstract classes? If I set DataType to an abstract class, it seems to still work fine. Perhaps this is just a bug? Also, related, inside DataTemplate, when I try to {x:Bind} it filters out inherited properties, so if I have Item : Base, and Base has a property Name, and DataType="Item", it filters out property Name and if I use it anyway, it seems to resolve to the class name. Did I miss something in the docs? Should I be making special non-abstract wrapping classes for every type I want to bind to xaml controls?

After my testing, it seems that inherited interface-properties are not recognized by the compiler when using the X:Bind. But it applies to abstract classes.
You could follow the sample to check your steps.
XAML code:
<ListView x:Name="List" ItemsSource="{x:Bind Fruits}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Fruit">
<TextBlock Text="{x:Bind price}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Code behind:
public sealed partial class MainPage : Page
{
public ObservableCollection<Fruit> Fruits{get;set;}
public MainPage()
{
this.InitializeComponent();
Fruits = new ObservableCollection<Fruit>()
{
new Fruit(){name="apple",price=12},
new Fruit(){name="peach",price=15},
new Fruit(){name="pear",price=8},
new Fruit(){name="banana",price=31},
new Fruit(){name="grape",price=5}
};
}
}
public class Fruit: IFruit
{
public string name { get; set;}
}
public abstract class IFruit
{
public int price { get; set;}
}

Related

BindableProperty in ViewCell

I've transferred my ViewCell from the list-xaml to a separate one and now I struggle with binding the properties.
My ListView's item source is an ObservableCollection of "member".
public class Member
{
public string Name { get; set; }
public string Image { get; set; }
public string Description { get; set; }
}
So now I'm trying to figure out how to use the bindable property within my MemberViewCell.
The examples I read are pretty straight forward, I create a BindableProperty for Name as well as a normal property for Name, when Name is modified it will trigger the graphical update.
Can I create a BindableProperty with the basis of Member so that I don't need to write all those bindables/property changed for each property I want exposed?
Something like this:
public static readonly BindableProperty MemberSource = BindableProperty.Create("Member", typeof(Member), typeof(ListViewMemberCell), null, defaultBindingMode: BindingMode.OneWay, propertyChanged: MemberSourcePropertyChanged);
public Member Member
{
get { return GetValue(MemberSource) as Member; }
set
{
SetValue(MemberSource, value);
}
}
private static void MemberSourcePropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
ListViewMemberCell cell = (ListViewMemberCell)bindable;
/// Set the different properties to my labels/images based on member.
}
Naturally I tried this first but as expected I get a compile error (within the consumer of this component).
<ListView.ItemTemplate>
<DataTemplate>
<memberCell:ListViewMemberCell Member="{Bindable}"/>
</DataTemplate>
</ListView.ItemTemplate>
So, {Bindable} feels wrong and the error says so as well, but what I did when I had the ViewCell within the DataTemplate was to bind like this {Bindable Name} /// Member.Name...
I hope its just me misunderstanding the examples..
First you'll need to name it MemberSourceProperty. The Property suffix is a requirement for every prop you create.
<ListView ItemsSource="{Binding ListOfMembers}">
<ListView.ItemTemplate>
<DataTemplate>
<!-- This will bind to the object being iterated (Member) -->
<memberCell:ListViewMemberCell MemberSourceProperty="{Bindable .}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And of course you will need to load memberCell in the xaml headers. ListOfMembers should be an ObservableCollection<Member> that you have in the BindingContext of the View.
I'm assuming you will know what to do in the ListViewMemberCell class. To get the name or image, simply do: Member.Name Member.Image ..
Hope it helps

Xamarin forms (Cross-Platform) : Multiple type of cells in ListView

I am new to Xamarin. I have a requirement where I have to implement a ListView or say tableView that have multiple different type-size cells.
And I also have to add Header for a particular section of cells, and some of my custom cells have a horizontal scroll in it.
I have done this thing in iOS native UITableView before, but don't know how this done in Xamarin cross platform, can anyone help me out this?
You are looking for DataTemplateSelector, which is very well documented in the official Xamarin.Forms documentation.
The basics are that you create your own DataTemplateSelector class:
public class MyDataTemplateSelector : DataTemplateSelector
{
}
In that class you override OnSelectTemplate:
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
}
By checking the type of the item argument, you should be able to figure out which template to return.
So lets say you have a ViewModel for Dog and one for Cat and want to show a different DataTemplate for each of those. You would do something like:
public class DogCatTemplateSelector : DataTemplateSelector
{
public DataTemplate DogTemplate { get; set; }
public DataTemplate CatTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
if (item is DogViewModel)
return DogTemplate;
return CatTemplate;
}
}
Then you can consume this in your XAML:
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="dogTemplate">
<ViewCell>
... <---- define your look of dog template here
</ViewCell>
</DataTemplate>
<DataTemplate x:Key="catTemplate">
<ViewCell>
... <---- define your look of cat template here
</ViewCell>
</DataTemplate>
<local:DogCatTemplateSelector x:Key="dogCatTemplateSelector"
DogTemplate="{StaticResource dogTemplate}"
CatTemplate="{StaticResource catTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
Then simply set the ItemTemplate to your dogCatTemplateSelector instance you've defined in the resources on your ListView:
<ListView ItemsSource="{Binding DogsCatsCollection}" ItemTemplate="{StaticResource dogCatTemplateSelector}" />
Your ViewModel would then look something like:
public class Animal : INotifyPropertyChanged
{
}
public class DogViewModel : Animal
{
}
public class CatViewModel : Animal
{
}
public class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<Animal> DogsCatsCollection { get; }
= new ObservableCollection<Animal>();
}
Then you just populate DogsCatsCollection with instances of dogs and cats.

How to have DesignTime data in WinRT XAML?

How can I get DesignTime data in WinRT XAML so the designer shows sample data?
Simple enough.
Create a Model like this:
public class Fruit
{
public string Name { get; set; }
}
Create a base ViewModel like this:
public class BaseViewModel
{
public ObservableCollection<Fruit> Fruits { get; set; }
}
Create a real ViewModel like this:
public class RealViewModel : BaseViewModel
{
public RealViewModel()
{
if (!Windows.ApplicationModel.DesignMode.DesignModeEnabled)
LoadData();
}
public void LoadData()
{
// TODO: load from service
}
}
Create a fake-data ViewModel like this:
public class FakeViewModel : BaseViewModel
{
public FakeViewModel()
{
this.Fruits = new ObservableCollection<Fruit>
{
new Fruit{ Name = "Blueberry"},
new Fruit{ Name = "Apple"},
new Fruit{ Name = "Banana"},
new Fruit{ Name = "Orange"},
new Fruit{ Name = "Strawberry"},
new Fruit{ Name = "Mango"},
new Fruit{ Name = "Kiwi"},
new Fruit{ Name = "Rasberry"},
new Fruit{ Name = "Blueberry"},
};
}
}
Do this in your XAML:
<Page.DataContext>
<local:RealViewModel />
</Page.DataContext>
<d:Page.DataContext>
<local:FakeViewModel />
</d:Page.DataContext>
Have fun!
PS: you can also attempt to use d:DesignData.
That approach also works. I feel it is not as straight forward.
In the end, it's up to you how to do it.
Either way, don't miss out on DeisgnTime data!
Here is the d:DesignInstance sample:
I will also use Jerry's Fruit class, but I won't use MVVM here as you don't need that to make it works.
Basically, we need to create the data model class (e.g., ViewModel or Model) that we want to have design data (e.g., in this case, I create a child class, but you don't need to).
public class Fruit
{
public string Name { get; set; }
}
public class SampleFruit : Fruit
{
public SampleFruit()
{
Name = "Orange (Sample)";
}
}
Then in our XAML, we can use d:DataContext to bind the child class.
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"
DataContext="{Binding}"
d:DataContext="{Binding Source={d:DesignInstance Type=local:SampleFruit, IsDesignTimeCreatable=True}}">
<TextBlock Text="{Binding Name}"
HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="42"/>
</Grid>
Please note this line:
d:DataContext="{Binding Source={d:DesignInstance Type=local:SampleFruit, IsDesignTimeCreatable=True}}"
Now you should see your design time data on both Visual Studio Designer and Blend.
P.S. In Blend 2013, there is a data tab that let you create sample data as well.

Changing part of view at runtime

I show several movie items in an ObservableCollection using a typical listbox+datatemplate view.
However, I want, in the same page, to be able to quickly change the view to what I define a posterview (i.e. only the posterimages in a wrappanel).
The xaml-page uses a viewmodel as datacontext.
Is there a way to basically replace part of the XAML content with another?
And still keep as little code as possible in the codebehind of the view.
I've seen WPF examples that for example use a DataTrigger bound to a viewmodelproperty which is very clean,
such as this article
... but Windows Phone does not have a DataTriggers, correct?
I'm trying to go for an MVVM-ish approach, so as little code as possible in the view code-behind is required.
So I want to change this:
<ContentControl DataContext="{Binding CinemaShowsOverview }" Template="{StaticResource ListView}" />
To:
<ContentControl DataContext="{Binding CinemaShowsOverview }" Template="{StaticResource PosterView}" />
DataTemplates with a DataTemplateSelector would be the way to go around this problem.
Base Data Template Selector:
public class DataTemplateSelector : ContentControl
{
public virtual DataTemplate SelectTemplate(object item, DependencyObject container)
{
throw new NotImplementedException();
}
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
ContentTemplate = SelectTemplate(newContent, this);
}
}
Specialized Template Selector for your CinemaShowsOverview
public class CinemaShowsTemplateSelector : DataTemplateSelector
{
public DataTemplate ListTemplate
{
get;
set;
}
public DataTemplate PosterTemplate
{
get;
set;
}
public DataTemplate DefaultTemplate
{
get;
set;
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item == null)
return DefaultTemplate;
var viewModel = item as CinemaShowsOverview;
if (viewModel != null)
return viewModel.IsPoster ? PowerTemplate : ListTemplate;
else
return DefaultTemplate;
}
}
And then in XAML (replacing your current ContentControl):
<assets:CinemaShowsTemplateSelector PosterTemplate="{StaticResource PosterView}"
ListTemplate="{StaticResource ListView}"
Content="{Binding CinemaShowsOverview}">
Just to be pedantic, the blog you mention describes typed data-templates, not datatriggers (as the author class them). No, this feature is not available in Silverlight for WP7.
You could expose the template you requires as a string within your view model, i.e. a string that is either ListView or PosterView. You then bind your Template property to this view-model property via a value converter that provides the template, which it can access via your applications Resources.

XAML Resource with static items?

<Window.Resource>
<ResourceDictionary>
<local:SomeResourceWithObsCollection x:Key="MyItemWithCollection">
<local:SomeClass.Instance /> <!-- THIS DOES NOT WORK -->
</local:SomeResourceWithObsCollection>
</ResourceDictionary>
</Window.Resources>
I don't know how to get that line to work... I've tried doing <x:Static SomeClass.Instance />, but that also isn't allowed.
[ContentProperty("TheItems")]
public class SomeResourceWithObsCollection
{
public class SomeResourceWithObsCollection()
{
TheItems = new ObservableCollection<IMyInterface>();
}
public ObservableCollection<IMyInterface> TheItems { get; set; }
}
public class SomeClass : IMyInterface
{
private static SomeClass _instance = new SomeClass();
private SomeClass() { }
public SomeClass Instance { get { return _instance; } }
}
You can't do what you're asking to do in XAML as of right now. Perhaps future versions of XAML will account for this. You have to do it in the code behind, here is an example:
Adding a static object to a resource dictionary
The closest I can suggest is a combination of the CompositeCollection and using ListBoxItems (or some other equivalent) to wrap your static content (as I believe you can only pull static content into XAML using the {x:Static} markup extension)
This can be used in XAML as below:
<ListBox>
<ListBox.ItemsSource>
<CompositeCollection>
<ListBoxItem Content="{x:Static local:Example.One}" />
<ListBoxItem Content="{x:Static local:Example.Two}" />
</CompositeCollection>
</ListBox.ItemsSource>
</ListBox>