Xamarin Custom Control Command / CommandParameter question - xamarin

I've been working on creating a custom control for some time. After several iterations I've come to the conclusion that I am having a binding issue... As it is, when I place my control into a simple XAML page and execute the feature, it works just fine. However, when I need to instantiate numerous controls on a single page, i.e. into a collection, flexlayout, carouselview the Command and CommandParameter bindings get lost... and the ViewModel calls no longer occur.
My control is simple... think of a checkbox replacement. I place A 1x1 grid, with a frame (for the outline) and a label to place a single character... "A", "B", "C"... "1". "2". "3"... whatever you would require... I have bindable properties.. Text, TextColor, BorderColor, BackgroundColor, and "Selected".
So, now I need to have a page ask the question... "How do you feel about... whatever... Pick all that apply." Then I provide a list... with number or lettered items... The user can select none, any, or all... So I create a view with a series of questions, that have a list of "checkable" items... As I said above, the control works perfectly if it is in a standalone page... If I generate a List of these controls dynamically, Command and CommandParameter suddenly no longer work.
my test implementation looks something like the following... although in this case think something much simpler like a "lottery ticket" number chooser. In this case the ViewModel would have a simple ObservableCollection<string> PrimaryControlList; And, the CommandParamter will call a VM function along with the text of the control in order to track the items the user has selected.
<Frame x:Name="primaryFrame">
<FlexLayout x:Name="flexPrimary" BindableLayout.ItemsSource="{Binding PrimaryControlList}" Wrap="Wrap" Direction="Row" JustifyContent="SpaceAround" AlignItems="Start" AlignContent="Start" >
<BindableLayout.ItemTemplate>
<DataTemplate>
<local:NumberSelect Text="{Binding .}" Command="Binding DoSomethingWithThis" CommandParameter="{Binding .}"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</FlexLayout>
</Frame>
Can anyone provide guidance?

The command is in your ViewModel while the current BindingContext of FlexLayout is PrimaryControlList.
Solution:
First, give a name to your ContentPage, let's say it MyPage:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
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"
mc:Ignorable="d"
x:Name="MyPage"
x:Class="App266.MainPage">
Assume your page binds to a ViewModel and your binding of command should be:
<local:NumberSelect Text="{Binding .}" Command="{Binding BindingContext.ButtonCommand, Source={x:Reference MyPage}}" CommandParameter="{Binding .}"/>

Related

Change ToolbarItem Text/Icon parameters

I am new to MAUI. And I haven't done anything complex with Xamarin as well.
The application I am testing with, has Shell navigation. And I can't find a way to change the toolbar items.
I would like to be able to change either the FontSize of the text, or the color of the SVG image. And I do not want those changes to affect the rest of my application, if possible. But if there is no other way, I can live with it.
I have managed to add styling to buttons, Labels etc... If styling is an option to this, it will be even better.
If I am on the wrong path, if you point me out why, I will be also grateful.
Thank you in advance!
If you want to just change a part of pages in your project. You can try to use a <Shell.TitleView> instead of the <Shell.ToolBarItem> in the content pages you want. Such as:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiAppTest.MainPage">
<Shell.TitleView>
<HorizontalStackLayout Margin="5" HorizontalOptions="End">
<Label Text="hello" TextColor="Red" FontSize="Medium" VerticalOptions="Center" HorizontalOptions="End" />
<Image Source="your image" Margin="10" Background="green"/>
</HorizontalStackLayout>
</Shell.TitleView>
You can change style of TabBar in Style.xaml in path \Resources\Style\. Search in file for Shell.TabBarForegroundColor. You will get TargetType="Shell" and this is style for AppShell.xaml.

How can I keep a Xamarin label on screen with data bound to a Carosell Item

I have a
<CarouselView ItemsSource="{x:Static vm:MainPageViewModel.MyItems}">
<CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding ID}"/>
<Label Text="{Binding Progress}"/>
...
I would like the <Label Text="{Binding Progress}"/> not to scroll off screen, but to stay fixed (at the top) and update as the user swipes through the CarouselView items. If I move the label outside of the CarouselView, it won't be data bound.
How can I keep the label fixed onscreen, but bound to the curren CarouselView item? (Do I need to do this via code behind?)
You can achieve this easily by binding the text property of the Label to the property in your ViewModel which will also do binding to the CurrentItem property of CarouselView.
Can you try something like this:
<Label Text="{x:Static vm:MainPageViewModel.MyCurrentItem.Progress}"/>
<CarouselView CurrentItem="{x:Static vm:MainPageViewModel.MyCurrentItem}"
ItemsSource="{x:Static vm:MainPageViewModel.MyItems}"
... other code
>
... other code
</CarouselView>
and in your ViewModel create a new bindable property of the same type as your MyItems list items are.
I have updated my GitHub repo with the example of this kind of behaviour:
You can find GitHub repo here.
and particular view here.
And here is a GIF of a running demo:
Label is above the CarouselView, and when you swipe and change the selected item, the value of label is changing according to the change of CarouselView.
Hope this was helpful for you, wishing you lots of luck with coding!

Is there any advantage to using a <StackLayout> or <Grid>?

The code I have looks like this:
<ListView.Header>
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<StackLayout Padding="10,35,10,10" Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<local:ExtLabel ExtStyleId="Body" Text="Custom body Label"></local:ExtLabel>
<local:ExtLabel ExtStyleId="Header" Text="Custom hdr Label"></local:ExtLabel>
</StackLayout>
</StackLayout>
</ListView.Header>
Would it be possible to implement this with a <Grid> and does the <Grid> offer more flexibility than <StackLayout> ?
I think you might want to have a look at this excellent blog post about this. The source is an employee from Xamarin, even more so, one of the founders of Forms.
A few of the bullets in reference to Layouts:
DO NOT use a StackLayout to host a single child.
DO NOT use a Grid when a StackLayout suffices.
DO NOT use multiple StackLayouts when a Grid suffices.
DO use a Grid to achieve layering.
In regard to looks, you could achieve the same with both. In regard to performance, take a look at the full blog post (and these rules) to decide what is the better option.

Using a ListBox control in a DataTemplate

I have a simple WP7 Programm where I want to switch between displaying my model objects in a ListBox and a Diagramm.
I want to use Data Templates and a Selector Class which returns the correct template.
The selector takes a boolean property in the view model and returns ListBoxTemplate or DiagrammTemplate
My Page Resources looks like this:
<local:NewTemplateSelector x:Key="NewTemplateSelector">
<local:NewTemplateSelector.ListBoxTemplate>
<DataTemplate>
<StackPanel>
<ListBox
x:Name="MainListBox" Margin="6,205,35,136" ItemsSource="{Binding Acts}"
ItemTemplate="{Binding ElementName=Page, Path=Orientation,
Converter={StaticResource OrientationToListItemTemplate}}" />
</StackPanel>
</DataTemplate>
</local:NewTemplateSelector.ListBoxTemplate>
<local:NewTemplateSelector.DiagrammTemplate>
<DataTemplate>
<TextBlock Text="Diagramm"/>
</DataTemplate>
</local:NewTemplateSelector.DiagrammTemplate>
</local:NewTemplateSelector>
My content Panel has only 1 element:
<ContentControl ContentTemplate="{Binding IsDiagramm,
Converter={StaticResource NewTemplateSelector}}" HorizontalAlignment="Left" HorizontalContentAlignment="Left" />
I always get a blank screen when I run this.
My Selector class returns the correct template, I can see this in the debugger.
When I replace the Listbox in the template with a simple textblock, the textblock is displayed, so I suspect a problem with databinding.
But the listbox in the template works fine, when I insert it in my content panel without any data templates.
Any hints for me?
You may like to refer to this thread which discusses nested listboxes.
Listbox Inside listbox Databinding Problem

Extremely confused about ContentPresenter

FYI I'm pretty new to Silverlight.
Okay, so I want to build a simple user control that contains a button plus some additional XAML as specified by the client of the control.
I got searching on Google and found at least 30 different articles that were all very confusing; especially because they talk about styling animation, customizing other controls that you don't own, and other crap I'm not ready for yet.
This is what I did.
In VS 2010, I right clicked and added a new UserControl called MyControl
To the MyControl.xaml I changed the LayoutRoot to a StackPanel and added a Button inside it
In my MainPage.xaml I added an instance of MyControl
I added a TextBox as a child element of this instance
I tried to build and got an error that MyControl didn't support Direct Content
Googled some more..
I changed MyControl to inherit from ContentControl and updated the xaml
I added a ContentPresenter in the xaml to represent the client "custom content"
Okay, it builds and the TextBox shows up, but the Button is missing.
Here's the relevant section from MainPage.xaml
<my:MyControl HorizontalAlignment="Left" Margin="49,26,0,0" x:Name="myContentControl1" VerticalAlignment="Top" Height="550" Width="389">
<TextBox Height="72" HorizontalAlignment="Left" Margin="166,339,0,0" Name="textBox1" Text="TextBox" VerticalAlignment="Top" Width="460" />
</my:MyControl>
Here's the MyControl.xaml
<ContentControl x:Class="ContentControlTest.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480">
<StackPanel x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}" Orientation="Vertical">
<ContentPresenter/>
<Button Content="Button" Height="72" HorizontalAlignment="Left" Margin="78,254,0,0" Name="FooFoo" VerticalAlignment="Bottom" Width="160" />
</StackPanel>
</ContentControl>
And here is the MyControl.cs
using System.Windows.Controls;
namespace ContentControlTest
{
public partial class MyControl : ContentControl
{
public MyControl()
{
InitializeComponent();
}
}
}
The way I thought it worked was that the child elements of the control instance are set as the Content property on the ContentControl base class of MyControl. Then, ContentPresenter "pastes" that content into the MyControl.xaml wherever appears.
Although that does seem to be how it works, in the process it is "eating" the Button that I have defined in the MyControl.xaml.
I'm trying not to get into ControlTemplate etc that at this point unless it is absolutely necessary.
Can someone with a clue please tell me what I am doing wrong.
thanks!
That's because the Content of the control is the entire StackPanel you've written by hand; when you set a new Content, the StackPanel is replaced.
A ControlTemplate is necessary for this scenario, I think; it would be a very simple one after all. The starting point can be the default style of the content control; put the style inside a ResourceDictionary (for instance, in the <ContentControl.Resources> section of your user control), and you're ready to go; all you need to do is add a grid and button inside that template.
Note that the style I linked to sets itself as the default for any reachable ContentControl; to make it only apply to your control and not to any children that may appear inside it, add x:Key="someKey" to the Style and set the ContentControl's Style property explicitly to Style={StaticResource someKey}.
Let me know if you need additional information; also, I might be wrong and there may be an easier way, but I doubt it; the Content property is meant to behave exactly like what you described.

Resources