Populating a property of type ObservableCollection in XAML with static resources - silverlight-4.0

I have created a class that contains a property of type ObservableCollection. I am trying to create an instance of the class in XAML and fill this property with members. I keep getting an exception that class T can not be converted to ObservableCollection, but this exception only occurs when I am trying to populate the list with elements that were declared as static resources.
Anybody has an idea why?
The code is as follows:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mseg="clr-namespace:SegApp.Model.DataEntities.Segments;assembly=SegApp.Model.Silverlight">
<mseg:Dot xKey="d1"/>
<mseg:Dot xKey="d2"/>
<mseg:Dot xKey="d3"/>
<mseg:Dot xKey="d4"/>
<mseg:Segment xKey="seg1">
<mseg:Segment.Dots>
<StaticResource ResourceKey="d1"/>
<StaticResource ResourceKey="d2"/>
<StaticResource ResourceKey="d3"/>
<StaticResource ResourceKey="d4"/>
</mseg:Segment.Dots>
</mseg:Segment>
</ResourceDictionary>
The Class definition is:
public class Segment : Part
{
public ObservableCollection<Dot> Dots { get; set; }
public Segment()
{
Dots = new ObservableCollection<Dot>();
}
}
And the exception says:
"
Object of type bla.bla.bla.Dot can not
be converted to type
System.Collections.ObjectModel.ObservableCollection'1[bla.bla.bla.Dot]
"
Any ideas?

As is your code, each element of the collection must be a Dot, not a resource...
Each entry of the list in your xaml code must be something like
or perhaps try
somevalue
or
{staticResource xxx }
But there is still a problem. The 1st syntax is ok, the second can work if there is a simple content for Dot, but the 3rd can't run : tag means "create an instance of Dot". And a StaticResource means "create an instance of.. and give it a key".
So last syntax will certainly not work cause you can replace the instance created by the tag with the instance coming from the resource...
But give it a try. The main problem in your code is than you're trying to feel a collection of Dot with Resource, that can't work and the compiler is not ok.. try using tag to create entry. And then play a bit to see if you can refer the resources somewhere in these tags..

In order to use collections XAML syntax change your property and remove it's setter:
public class Segment : DependencyObject
{
private readonly ObservableCollection<Dot> _dots = new ObservableCollection<Dot>();
public ObservableCollection<Dot> Dots
{
get { return _dots; }
}
}

Related

Xamarin XAML x:Static reference to property in a nested class

I'm writing a mobile app using Xamarin and I have a static class called Strings that wraps my RESX resources. I want to use x:Static to bind to these in my XAML files. This is working if I have a single static class with static properties to bind to.
I'm cutting out some comments and other non-essential bits, but it basically looks like this:
namespace MyCompany.Resources
{
public static partial class Strings
{
public static string LabelUsername { get { return Resources.LabelUsername; } }
}
}
Then in my XAML, I bind to it like this:
<Entry Placeholder="{x:Static resources:Strings.LabelUsername}"
where resources is defined as
xmlns:resources="clr-namespace:MyCompany.Resources;assembly=MyCompany"
That all works fine. It breaks down when I add a nested class to Strings. The class looks like this:
namespace MyCompany.Resources
{
public static partial class Strings
{
public static partial class Label
{
public static string Username { get { return Resources.Label_Username; } }
}
}
}
If I do that, then I would bind to it in my XAML like this:
<Entry Placeholder="{x:Static resources:Strings.Label.Username}"
Notice how after "resources:" we now have three levels (Strings.Label.Username). This seems to be what fails. When I do this, I get the compile error:
Type Strings.Label not found in xmlns clr-namespace:MyCompany.Resources;assembly=MyCompany
Also, I can access the nested class and its properties just fine from my ViewModels. Is there any way to make this work from the XAML? I know I could bind to a variable in the VM and then have that reference Strings.Label.Username, but I don't want to do that for every resource binding.
Your static property's name in the binding should be
Strings+LabelUsername.Username
Not only did you have a typo, but you tried to use the dot notation to reference the nested class, which won't work.
Bindings use standard .net reflection notation for referencing properties and classes by name (they either use a parser on the string or use reflection directly, can't be arsed to check the codebase). Nested class names use a + to separate the containing class name and the inner class name. You can read more about that here:
C# : having a "+" in the class name?

Ignore the Binding initialization

The inital problem is coming from a personal project about the polyline of the Xamarin.Forms.Map where the initialization is realized by a binding from the XAML part..
Let me be clear by an example :
I have an object CustomMap.cs which inherit from Xamarin.Forms.Map (This file is in the PCL part -> CustomControl/CustomMap.cs)
public class CustomMap : Map, INotifyPropertyChanged
{
public static readonly BindableProperty PolylineAddressPointsProperty =
BindableProperty.Create(nameof(PolylineAddressPoints), typeof(List<string>), typeof(CustomMap), null);
public List<string> PolylineAddressPoints
{
get { return (List<string>)GetValue(PolylineAddressPointsProperty); }
set
{
SetValue(PolylineAddressPointsProperty, value);
this.GeneratePolylineCoordinatesInner();
}
}
// ...
}
As you can see, I have a bindable property with an assessor and the XAML doesn't seem to use this assessor..
So the MainPge.xaml part of the page, where the control is called, looks like that:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:control="clr-namespace:MapPolylineProject.CustomControl;assembly=MapPolylineProject"
x:Class="MapPolylineProject.Page.MainPage">
<ContentPage.Content>
<control:CustomMap x:Name="MapTest" PolylineAddressPoints="{Binding AddressPointList}"
VerticalOptions="Fill" HorizontalOptions="Fill"/>
</ContentPage.Content>
</ContentPage>
The MainPge.xaml.cs part:
public partial class MainPage : ContentPage
{
public List<string> AddressPointList { get; set; }
public MainPage()
{
base.BindingContext = this;
AddressPointList = new List<string>()
{
"72230 Ruaudin, France",
"72100 Le Mans, France",
"77500 Chelles, France"
};
InitializeComponent();
//MapTest.PolylineAddressPoints = AddressPointList;
}
}
So, everything is fine if I edit the PolylineAddressPoints from the object instance (if the commented part isnt' commented..), but if I init the value from the XAML (from the InitializeComponent();), it doesn't work, the SetValue, in the Set {}, of the CustomMap.PolylineAddressPoints, isn't called..
I then searched on the web about it and get something about the Dependency Properties? or something like that. So I tried some solutions but, from WPF, so some methods, such as DependencyProperty.Register();. So yeah, I can't find the way to solve my problem..
I also though about something, if DependencyProperty.Register(); would exists in Xamarin.Forms, then it means I would have to do it for each values? Because, if every value has to be set by a XAML binding logic, it would not work, I would have to register every value, doesn't it?
I'm sorry if I'm not clear, but I'm so lost about this problem.. Please, do not hesitate to ask for more details, thank in advance !
Finaly, the initial problem is that I'm trying to set a value of an object/control, from the XAML. Doing this by a Binding doesn't work, it seems like it ignored.. However, it does work if I do the following:
MapTest.PolylineAddressPoints = AddressPointList;
There are multiple questions in this:
Why is the property setter never called when using Xaml ?
Am I properly defining my BindableProperty ?
Why is my binding failing ?
Let me answer them in a different order.
Am I properly defining my BindableProperty ?
The BindableProperty declaration is right, but could be improved by using an IList<string>:
public static readonly BindableProperty PolylineAddressPointsProperty =
BindableProperty.Create(nameof(PolylineAddressPoints), typeof(IList<string>), typeof(CustomMap), null);
but the property accessor is wrong, and should only contains this:
public IList<string> PolylineAddressPoints
{
get { return (IList<string>)GetValue(PolylineAddressPointsProperty); }
set { SetValue(PolylineAddressPointsProperty, value); }
}
I'll tell you why while answering the next question. But you want to invoke a method when the property has changed. In order to do that, you have to reference a propertyChanged delegate to CreateBindableProperty, like this:
public static readonly BindableProperty PolylineAddressPointsProperty =
BindableProperty.Create(nameof(PolylineAddressPoints), typeof(IList<string>), typeof(CustomMap), null,
propertyChanged: OnPolyLineAddressPointsPropertyChanged);
And you have to declare that method too:
static void OnPolyLineAddressPointsPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
((CustomMap)bindable).OnPolyLineAddressPointsPropertyChanged((IList<string>)oldValue, (IList<string>)newValue);
}
void OnPolyLineAddressPointsPropertyChanged(IList<string> oldValue, IList<string> newValue)
{
GeneratePolylineCoordinatesInner();
}
Why is the property setter never called when using Xaml ?
The property, and the property accessors, are only meant to be invoked when accessing the property by code. C# code.
When setting a property with a BindablePrperty backing store from Xaml, the property accessors are bypassed and SetValue() is used directly.
When defining a Binding, both from code or from Xaml, property accessors are again bypassed and SetValue() is used when the property needs to be modified. And when SetValue() is invoked, the propertyChanged delegate is executed after the property has changed (to be complete here, propertyChanging is invoked before the property change).
You might wonder why bother defining the property if the bindable property is only used by xaml, or used in the context of Binding. Well, I said the property accessors weren't invoked, but they are used in the context of Xaml and XamlC:
a [TypeConverter] attribute can be defined on the property, and will be used
with XamlC on, the property signature can be used to infer, at compile time, the Type of the BindableProperty.
So it's a good habit to always declare property accessors for public BindableProperties. ALWAYS.
Why is my binding failing ?
As you're using CustomMap as both View and ViewModel (I won't tell the Mvvm Police), doing this in your constructor should be enough:
BindingContext = this; //no need to prefix it with base.
As you're doing it already, your Binding should work once you've modified the BindableProperty declaration in the way I explained earlier.

Page with type parameter

I would like to use new feature of UWP -> x:Bind. In order to that, all my pages need to have ViewModel property (as described in tutorials).
To avoid code duplicity, I have established base class as follows:
public abstract class BasePage<TBaseVM> : Page, where TBaseVM : BaseVM
{
public TBaseVM VM { get; private set; }
protected BasePage()
{
DataContextChanged += (s, e) => VM = e.NewValue as TBaseVM;
}
}
As you can see this BasePage class contains property called "VM" and property is of type BaseVM. Hence, I don't need to define VM property on each derived class.
Then I created derived page 'MainPage' defined in xaml as follows:
<pages:BasePage
x:Class="Realarm.View.Pages.MainPage"
x:TypeArguments="viewModel:MainVM">
By doing that, even Resharper's Intellisense offers me properties from "MainVM" in MainPage.xaml, thus is can write:
<ListView ItemsSource="{x:Bind VM.AlarmsVM}">
Unfortunately, when I try to build the project, I get error in MainPage.g.i.cs:
Severity Code Description Project File Line
Error CS0305 Using the generic type 'BasePage' requires 1 type arguments Realarm D:...\Realarm\obj\x86\Debug\View\Pages\MainPage.g.i.cs 13
Any help?
I got this working using Xamarin.Forms.
Base Page:
public abstract class BaseContentPage<TViewModel> : ContentPage where TViewModel : BaseViewModel, new()
HomePage.cs:
public partial class HomePage : BaseContentPage<HomeViewModel>
HomePage.xaml:
<d:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="clr-namespace:Sample.Pages;assembly=Sample"
xmlns:vm="clr-namespace:Sample.ViewModels;assembly=Sample"
x:Class="Sample.Pages.HomePage"
x:TypeArguments="vm:HomeViewModel">
<ContentPage.Content>
</ContentPage.Content>
</d:BaseContentPage>
Just add a x:TypeArguments definition at the top of the XAML:
<v:BasePage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:v="clr-namespace:YourApp.Views"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:YourApp.ViewModels"
mc:Ignorable="d"
x:TypeArguments="vm:HomeViewModel"
x:Class="YourApp.MainPage">
Worked for me as well when I set the BindingContext as given below in Base Page's constructor:
public BasePage()
{
BindingContext = new TBaseVM();
}

How to access Pivot.TitleTemplate as UserControl?

Subj, how can i get it?
<controls:Pivot.TitleTemplate>
<DataTemplate>
<mainPivot:MyUserControl Name="MainPivotHeader"/>
</DataTemplate>
</controls:Pivot.TitleTemplate>
Tried to find it via VisualTreeFinders, but it sees only pivot item.
UserControl shows a picture, but it depends on user. During first initialization, it is empty, because user is not yet logged in. So, i'd like to force its update.
I can use mvvm light messaging, but i'm looking for self-sufficient components. This forcing is rare, so i dont want to use messaging here.
You should bind the Title property of the Pivot to a property on a ViewModel. Your DataTemplate would then have it's DataContext already set to that object. When you need to refresh, you call some method on that object.
Example
public class ViewModel : INotifyPropertyChanged
{
private MyTitleObject _titleObject;
public MyTitleObject TitleObject
{
get { return _titleObject; }
set
{
_titleObject = value;
OnPropertyChanged("TitleObject");
}
}
public void Refresh()
{
TitleObject = new MyTitleObject();
// or refresh values directly on the object
}
...
}
You xaml for your Pivot would need to following
<controls:Pivot Title="{Binding TitleObject}">
</controls:Pivot>
When you want to refresh, call the refresh on the viewmodel.

Caliburn.micro design time data for WinRT - object reference not set to an instance of an object

I'm doing windows 8 app dev using the caliburn.micro MVVM framework.
I'm having issues with design time data. I've looked high and low through various blogs and what not to find an answer. No luck so far.
Here is a section from my view where I say use this view model for design time
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:cal="using:Caliburn.Micro"
xmlns:vm="using:MyApp.SampleViewModels"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:SampleNewsViewModel, IsDesignTimeCreatable=True}"
cal:Bind.AtDesignTime="True">
The d:DataContext... part is being underlined and saying "object reference not set to an instance of an object"
I have a view model with a default constructor
namespace MyApp.SampleViewModels
{
public sealed class SampleNewsViewModel
{
public SampleNewsViewModel()
{
Title = "News Title";
}
private string _title;
public string Title
{
get { return _title; }
set { _title = value; }
}
}
}
Pretty sure there's nothing wrong with my ViewModel (but I could be wrong).
I can't figure this out, any point in the right direction would be awesome.
cheers,
Lochana
First off, make sure you have got the namespaces configured correctly (this gives me a headache many times). Now with that out of our way, i can tell you to try too things:
Try to add these to your namespace declarations mc:Ignorable="d" and xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006".
If this alone didn't work then try to replace
Type=vm:SampleNewsViewModel with Type={x:Type
vm:SampleNewsViewModel and see if it works.
Oh man...I found the issue, and it's my fault.
In my design time view model, I had not initialized the list, and in the constructor, was trying to add items to it.
This fixed it
private List<NewsItem> _itemListView = new List<NewsItem>();
public List<NewsItem> ItemListView
{
get { return _itemListView; }
set { _itemListView = value; }
}
The lesson I learned here is that the error message "Object reference not set to an instance of an object" can mean you're view model is broken, even though it doesn't explicitly say. So for anyone starting out with caliburn.micro, unit test your design time view models to make sure they work as expected.