Xamarin custom control in a datatemplate created with CreateContent() - xamarin

I have implemented a custom DataTemplateSelector according to: GitHub Xamarin Forms.
This allows for a datatemplate to be selected based on an item, which is received through a data binding. This works fine to select a proper datatemplate and render it. However, I am now at a point where I want to add custom controls to this datatemplate (custom buttons). This works on any other page, but for some reason not in this datatemplate.
The relevant lines in the datatemplate:
xmlns:controls="clr-namespace:Universal_ONE.Views.Controls"
<controls:IconButton Command="{Binding RobotLocationSave}"
Image="{StaticResource BoltBlack}"/>
The part the datatemplate is selected and created:
var templateToUse = templateSelector.SelectTemplate(item, null);
View view = (View)templateToUse.CreateContent();
view.BindingContext = bindingContext;
In the catch block I get the error (after calling CreateContent()):
Xamarin.Forms.Xaml.XamlParseException: Position 371:26. Type controls:IconButton not found in xmlns clr-namespace:Universal_ONE.Views.Controls
The problem has to be with the CreateContent(), since using the control outside of this datatemplate works.
EDIT 1
I've created a Minimal Working Example: GitHub.
The datatemplate is now hardcoded, so I'm sure that one is selected.
The same parseexception gets thrown.
I am trying to compile Xamarin.Forms myself so I can debug the framework itself. However, compiling is not going smooth; thus might take a bit longer.
EDIT 2
A bit more info on the MWE:
The MainPage.xaml has the default Xamarin.Forms app code. Below I've added a custom control, which is simply a frame with a label. The text of this label is set via a bindableproperty (not really relevant). Below this control is the datatemplatecontrol added, which in turn calls the datatemplateselector, which returns the datatemplate. This datatemplate contains the same control as the mainpage. Thus the rendered app should have 2 controls. However, in de datatemplatecontrol you can breakpoint on the catch statement (look for my comment). This is where the parseexception will show, which is caused after calling CreateContent() on the datatemplate.
EDIT 3
I've not been clear enough I think. But you have to put a breakpoint on line 41 of datatemplatecontroler.cs. Since the content of the datatemplate is set to null if the createcontent() fails, thus fails gracefully. When hitting the breakpoint you can read the parseexception.
EDIT 4
I've made an issue and a pull request on the Xamarin repository: GitHub. The problem resides with the XamlParser, which has a small bug where it selects the wrong assembly. Inside the issue I've mentioned a workaround which can be used for now.

I try to download your example and run it. When I run to Content = CreateTemplateForItem(ItemTemplate); the program does not report an error, so the program skips the catch method. Here is a screenshot of the runtime:
Then I tried to actively throw an exception in the try statement (throw new Exception();) to make the program enter the catch method, Here is a screenshot of the runtime:

Related

Calling InitializeComponent in OnAppearing method in Xamarin Forms Project

Is there any reason or problem I cant call InitializeComponent method in OnAppearing function of a page in xamarin forms project?
I understand that I must call InitializeComponent only once to create the actual page. But what if I check that Content is already created and do it as below. Is it a bad implementation or practice? because it is said that no xaml based application does it and always call it in a constructor of the page.
reason I want to do it as below because xamarin.forms start up time is slow running on Android and if you use Masterdetail page(I think same for tabbed page), you must initialize it at the start up, it causes every navigation page defined in masterdetail page to be initialized and it costs you 2-3 secs depending on your UI could be even higher cost. any thoughts or experiences on this?
protected override void OnAppearing()
{
if (Content == null)
{
InitializeComponent();
}
}
I do not recommend this approach. From the xamarin docs.
The constructor of that class calls InitializeComponent, which then calls the LoadFromXaml method that extracts the XAML file (or its compiled binary) from the PCL. LoadFromXaml initializes all the objects defined in the XAML file, connects them all together in parent-child relationships, attaches event handlers defined in code to events set in the XAML file, and sets the resultant tree of objects as the content of the page.
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/getting_started_with_xaml/
If your forms are too slow on Android I would enable Fast Renders instead
In the OnCreate of your main activity add this line of code just before Xamarin.Forms.Init
Forms.SetFlags("FastRenderers_Experimental");
https://xamarinhelp.com/xamarin-forms-fastrenderers-android/
It would not hurt to try to use compiled Xaml also
using Xamarin.Forms.Xaml;
...
[XamlCompilation (XamlCompilationOptions.Compile)]
public class HomePage : ContentPage
{
https://developer.xamarin.com/guides/xamarin-forms/xaml/xamlc/

Sort ListView bound to ObservableCollection in XAML

I have a ListView that is bound through XAML to an ObservableCollection. I used to do the sorting in code behind but there are many operations where I would have to keep in mind that the sorting needs to be refreshed.
So I wanted to make it more clean and make the sorting directly in XAML more dynamically.
Unfortunately I cannot find tags/attributes to do this. All examples I find are for WPF applications using <CollectionViewSource.SortDescription> which seems to be not supported in XAML, at least for now.
I found a AdvancedCollectionView control in the UWP Community Toolkit which I then used. This control uses the existing ObservableCollection and the ListView (source) is now bound to this AdvancedCollectionView.
It seems to work - so what's the problem.
1) I now have to use a huge Nu-Get package with many licenses (which I should have read :-) )
2) I implemented INotifyPropertyChanged for the class that is bound to the ListView (the single item) so that the ListView gets refreshed when a property, e.g. the Title of an item changes. This used to work earlier, before I switched to AdvancedCollectionView but now the event raiser throws a NullReferenceExcpetion.
So I'm now asking myself if there isn't really a more Out-of-the-Box way to do the simple task of sorting a ListView by a bound property?
Apparently the issue with the AdvancedCollectionView occurs because there is currently a bug when no Filter property has been set.
I replaced the NuGet-Package with the complete source and debugged it to locate the error in the ItemOnPropertyChanged where it threw the NullReferenceException on line:
filterResult = _filter(item);
because "_filter" was null.
I reporeted the bug and it looks like a fix will be in version 2.2 of the Community Toolkit:
https://github.com/Microsoft/UWPCommunityToolkit/issues/1686

Wicket replacing panel with ajax fails with MarkupNotFoundException

the page markup has
<div wicket:id="stepPanel" />
tag in it and when the page is first loaded it works great that is
add(new MyFirstPanel("stepPanel"));
works fine. But then when I trigger an Ajax event and request redrawing
addOrReplace(new MySecondPanel("stepPanel"));
target.add(MyPage.this);
i get the following error
Last cause: Failed to find markup file associated. MyFirstPanel: [MyFirstPanel [Component id = stepPanel]]
please note that it tries to find the wrong markup (should look for markup for MySecondPanel) and it fails regardless it succedded to do so before!
I instantiate panels using reflection, but could it be a problem here? No exceptions thrown.
Anwser:
Actually it was something else - I have noticed that one of my AjaxSubmitLinks had reference to a form that was no longer placed in a markup... so whatever you do just remember not to leave that reference.
You are adding MyPage after replacing the Panel causing MyPage to re-render.
There is a good example on how to replace panels here.
Yes you can call panels via reflection. I don't clearly know what you are trying to do with event here but if you want you can attach your panel with AjaxSelfUpdatingTimerBehavior and define the duration which will update this component in the given time period.
Hope its useful.

Panorama Control not showing data

I am making a Panorama Windows Phone 8 app. This is the first time I have actually used one in an app.
I am having problems showing the data in runtime. Instead I am only seeing a list:
RuntimeOne
RuntimeTwo
RuntimeThree
etc..
I don't have a clue what has happened, it worked the other day. I am going into the SampleData folder and changing LineOne, LineTwo, LineThree, etc but it's not doing anything when I deploy the app to the Windows Phone Emulator.
What's happening is that there are two different sets of data, and the DataContext at runtime is different from design time.
The data that you see in design mode ('design one', 'design two') is
stored in MainViewModelSampleData.cs, so changing that doesn't affect
the runtime experience.
The data at runtime is coming from the LoadData method in
MainViewModel.cs
At the top of MainPage.xaml, you'll see
d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}"
and the "d" namespace here is a mnemonic for 'design'. If you look at the sample data XAML file, you'll notice it declares a class called MainViewModel with a collection of Items.
At runtime, MainViewModel.cs (specifically the LoadData method) adds items one by one to the Items property of the MainViewModel class, and that class is in turn set to be the runtime DataContext in the constructor of MainPage
The panorama control itself has markup like
<phone:LongListSelector Margin="0,0,-22,0" ItemsSource="{Binding Items}">
so it's expecting to see a collection called Items on whatever the current DataContext is, and the fact two different data contexts are in play explains what you're seeing.
The data binding magic is incredibly cool and powerful, but sometimes does leave you scratching your head.

MvvmCross : databinding programmatically

I think I need to bind data programmatically to solve my problem.
I use a TabHost which hosts 2 Tabs.
I need to load the MvxBindableListView in the second tab when TabHost appears and keep the first tab as default tab.
What I'm doing is starting the second tab activity without problem because I check the process using this code:
protected override void OnViewModelSet()
{
SetContentView(Resource.Layout.Page_ActivityView);
System.Diagnostics.Debug.WriteLine("activityView started");
MvxBindableListView mvxBindableListView = FindViewById<MvxBindableListView>(Resource.Id.mvxBindableListView);
mvxBindableListView.ChildViewAdded += new System.EventHandler<Android.Views.ViewGroup.ChildViewAddedEventArgs>(mvxBindableListView_ChildViewAdded);
}
"activityView started" is displayed in output debugger, but MvxBindableListView.ChildViewAdded event isn't raised, only when I click the second tab.
So I suppose that MvxBindableListView is not databound.
Thanks in advance to help me loading my second tab programmatically.
I think ChildViewAdded is an event that occurs when the ListView is rendered - at that time when it needs to 'draw list items', then it will ask its adapter for child Views for the screen. As you scroll down the list, it will then ask for more child views - but it will also reuse views - so for a simple list you should only ever (hopefully) get N+1 calls on ChildViewAdded for a list which shows N items at one time.
So it's perfectly possible for a list to be databound but never to call ChildViewAdded - that won't get called until the list is 'drawn'
Sadly the Xamarin docs aren't helpful here - http://docs.mono-android.net/monodoc.ashx?link=E%3AAndroid.Views.ViewGroup.ChildViewAdded
Note: if you do actually want to bind programatically, then you can do this to - using Bind() methods and extension methods. However, I haven't expanded on that here - as it doesn't sound like that's actually what you need!

Resources