Override Content on ContentView - xamarin

I am trying to do something i thought would be simple but i havent been able to find the solution yet.
I have a pretty basic custom element for adding a thick border within a frame.
XAML
<?xml version="1.0" encoding="UTF-8" ?>
<Frame
x:Class="ResumeApp.CustomElements.BetterFrame"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Name="OuterFrame"
HorizontalOptions="FillAndExpand"
OutlineColor="Black">
<Frame
x:Name="InnerFrame"
HorizontalOptions="FillAndExpand"
OutlineColor="Black" />
</Frame>
CodeBehind
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace ResumeApp.CustomElements
{
[XamlCompilation(XamlCompilationOptions.Compile)]
[ContentProperty(nameof(Content))]
public partial class BetterFrame : Frame
{
private int _Thickness = 0;
public int Thickness
{
get { return _Thickness; }
set
{
Padding = new Thickness(value); _Thickness = value;
}
}
public float Corner
{
get { return InnerFrame.CornerRadius; }
set
{
InnerFrame.CornerRadius = value; OuterFrame.CornerRadius = value;
}
}
public new View Content
{
get
{
//Breakpoint not hit here
return (View)GetValue(ContentProperty);
}
set
{
//breakpoint not hit here
SetValue(ContentProperty, value);
}
}
public Color InnerColor { get { return InnerFrame.BackgroundColor; } set { InnerFrame.BackgroundColor = value; } }
public Color OuterColor { get { return OuterFrame.BackgroundColor; } set { OuterFrame.BackgroundColor = value; } }
public new Color BorderColor { get { return InnerFrame.BorderColor; } set { InnerFrame.BorderColor = value; OuterFrame.BorderColor = value; } }
public BetterFrame()
{
InitializeComponent();
}
protected override void OnParentSet()
{
base.OnParentSet();
for (Element Parent = this.Parent; Parent != null; Parent = Parent.Parent)
{
try
{
Color background = Parent.GetPropertyIfSet<Color>(BackgroundColorProperty, Color.Transparent);
if (background != Color.Transparent && InnerFrame.BackgroundColor != Color.Transparent)
{
InnerFrame.BackgroundColor = background;
break;
}
}
catch
{
}
}
}
}
}
So using the code above when there is no content inside the frame everything looks as expected but once i add content it overwrites the InnerFrame . is there any way of making it so that when i add Content i add it to the InnerFrame instead of the Outter Frame. I added the Content Property to try and catch it being set but i never hit breakpoints set on it so i dont think it is being used.

If adding conetent in another Xaml , it will override the inner Frame . Becasue added content is a child view for outer Frame , and Fram only can own one child view . So the inner Frame will be override .
<local:BetterFrame>
<StackLayout BackgroundColor="AliceBlue">
<Entry x:Name="myentry2"
Text="Second Entry" />
</StackLayout>
</local:BetterFrame>
Here will not show the inner Frame :
Therefore , if adding Content in BetterFrame directly , it will show .
<?xml version="1.0" encoding="utf-8" ?>
<Frame 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="OuterFrame"
x:Class="AppEntryTest.BetterFrame"
HorizontalOptions="FillAndExpand"
OutlineColor="Black">
<Frame x:Name="InnerFrame"
HorizontalOptions="FillAndExpand"
OutlineColor="Black">
<StackLayout BackgroundColor="AliceBlue">
<Entry x:Name="myentry2"
Text="Second Entry" />
</StackLayout>
</Frame>
</Frame>
The effect :
=============================Update================================
Create a custom ContentView and contain Outer Frame and Ineer Frame :
<?xml version="1.0" encoding="UTF-8"?>
<ContentView 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:Class="AppEntryTest.Controls.CustomFrameView">
<ContentView.ControlTemplate>
<ControlTemplate >
<Frame x:Name="FrameExtContainer"
Padding="5"
HasShadow="False"
HorizontalOptions="FillAndExpand"
CornerRadius="5"
OutlineColor="Black"
BorderColor="Black">
<Frame x:Name="FrameIntContainer"
Padding="15"
Margin="12"
HasShadow="False"
HorizontalOptions="FillAndExpand"
CornerRadius="5"
OutlineColor="Black"
BorderColor="Black">
<ContentPresenter />
</Frame>
</Frame>
</ControlTemplate>
</ContentView.ControlTemplate>
</ContentView>
Now used in another Xaml can work:
<local:CustomFrameView>
<StackLayout BackgroundColor="AliceBlue">
<Entry x:Name="myentry3"
Text="Third Entry" />
</StackLayout>
</local:CustomFrameView>
The effect :

Related

.NET MAUI CollectionView dont get populated

I made a very simple .NET MAUI App based on the ClickMe Code thats generated by VS2022.
The "ClickMe" Button should add a entry to a CollectionView which is binded to a ObservableCollection, but it don't populate the view if click the button although "monkeys" are added to the ObservableCollection maybe somebody can help what I'm missing.
public class Monkey
{
public string Name { get;set; }
public Monkey(string name) {Name = name; }
}
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiObservableList.MainPage">
<ScrollView>
<VerticalStackLayout Spacing="25" Padding="30,0" >
<Button x:Name="CounterBtn" Text="Click me" Clicked="OnCounterClicked" HorizontalOptions="Center" />
<CollectionView ItemsSource="{Binding Monkeys}">
<CollectionView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
MainPage.xaml.cs:
int count = 0;
public ObservableCollection<Monkey> Monkeys { get; set; } = new ObservableCollection<Monkey>();
public MainPage()
{
InitializeComponent();
BindingContext = this;
}
private void OnCounterClicked(object sender, EventArgs e)
{
count++;
if (count == 1)
CounterBtn.Text = $"Clicked {count} time";
else
CounterBtn.Text = $"Clicked {count} times";
Monkeys.Add(new Monkey(CounterBtn.Text));
}
Replace TextCell with a Label. TextCell only works with ListView

Xamarin swipeview, when you swipe again, it returns to the position of the last. What is this?

If you swipe several times on an already opening field, then after closing the swiped element will return to the position from which the last swipe was.
For example:
I upload project for replay problem: link
XAML Code:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestSwipeView.MainPage">
<CollectionView x:Name="collectionView"
ItemsSource="{Binding TestSequence}">
<CollectionView.ItemTemplate>
<DataTemplate>
<SwipeView>
<SwipeView.LeftItems>
<SwipeItems>
<SwipeItem Text="F"
BackgroundColor="LightGreen"/>
<SwipeItem Text="D"
BackgroundColor="LightPink"/>
</SwipeItems>
</SwipeView.LeftItems>
<Grid BackgroundColor="White"
Padding="10">
<Label Text="{Binding .}"></Label>
</Grid>
</SwipeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>
Xaml.cs code:
namespace TestSwipeView
{
public partial class MainPage : ContentPage
{
public List<String> TestSequence { get; set; }
public MainPage()
{
TestSequence = new List<string>();
for (int i=0; i<10; i++)
{
TestSequence.Add(String.Format("Test {0}",i));
}
InitializeComponent();
this.BindingContext = this;
}
}
}

How to make a local ContentView bind a Label text

I made a ContentView with an Expander. I want to duplicate this Xaml code multiple times in an other Xaml file. But I want for each Expander a different Binding.
I linked this code (ContentView code):
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:expandable="clr-namespace:Expandable;assembly=ExpandableView"
x:Class="Hello.ExpandableView">
<ContentView.Content>
<expandable:ExpandableView Padding="5">
<expandable:ExpandableView.PrimaryView>
<Frame BackgroundColor="White" Padding="5" CornerRadius="10">
<StackLayout Orientation="Horizontal">
<Label x:Name="label" FontSize="16" TextColor="Black" FontAttributes="Bold" Padding="5" HorizontalTextAlignment="Start" HorizontalOptions="StartAndExpand"/>
<Image Source="arrow.png" WidthRequest="25" HeightRequest="25" Margin="5"/>
</StackLayout>
</Frame>
</expandable:ExpandableView.PrimaryView>
<expandable:ExpandableView.SecondaryViewTemplate>
<DataTemplate>
<Frame BackgroundColor="White" Padding="5" CornerRadius="10">
<Label Text="{Binding Tip1Uitleg}" FontSize="15" TextColor="Black" Padding="5"/>
</Frame>
</DataTemplate>
</expandable:ExpandableView.SecondaryViewTemplate>
</expandable:ExpandableView>
</ContentView.Content>
</ContentView>
namespace Hello
{
public partial class ExpandableView : ContentView
{
public static BindableProperty LabelProperty =
BindableProperty.Create(nameof(Label),
typeof(string),
typeof(ExpandableView),
propertyChanged: (b, o, n) => (b as ExpandableView).OnLabelChanged());
private void OnLabelChanged()
{
label.Text = Label; //label is the x:Name of your Label control in ExpandableView.xaml
}
public string Label
{
get => (string)GetValue(LabelProperty);
set => SetValue(LabelProperty, value);
}
}
}
To this code:
This works:
<local:ExpandableView Label="Hello"/>
But I want this. This does not work:
<?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:expandable="clr-namespace:Expandable;assembly=ExpandableView"
xmlns:local="clr-namespace:Hello"
x:Class="Hello.Health.HealthDetail"
Title="{Binding Name}">
<ContentPage.Content>
<ScrollView>
<StackLayout>
<Image Source="{Binding Image}"/>
<Label Text="{Binding Tip1Uitleg}" FontSize="15" TextColor="Black" Padding="10, 5, 10, 5"/>
<local:ExpandableView Label="{Binding Tip1}"/> //This is what it is all about
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>
CodeBehind:
namespace Hello.Health
{
public partial class HealthDetail : ContentPage
{
public HealthDetail(HealthStrings healthStrings)
{
if (healthStrings == null)
throw new ArgumentNullException();
BindingContext = healthStrings;
InitializeComponent();
}
}
}
How do I make this work? I have to make this above more dynamic, but I do not know how.
BTW This also works:
<Label Text="{Binding Tip1}" />
I am sorry for the unclear explanation, I hope someone can help me.
Thank you for your time :)
You should not bind to 'this' since you are using a BindableProperty. Instead you should update the Label.Text property when the value of the BindableProperty changes.
public static BindableProperty LabelProperty =
BindableProperty.Create(nameof(Label),
typeof(string),
typeof(ExpandableView),
propertyChanged:(b, o, n) => (b as ExpandableView).OnLabelChanged());
private void OnLabelChanged()
{
label.Text = Label; //label is the x:Name of your Label control in ExpandableView.xaml
}
public string Label
{
get => (string)GetValue(LabelProperty);
set => SetValue(LabelProperty, value);
}
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="YourProject.ExpandableView">
<Grid>
<Label x:Name="label"/>
</Grid>
</ContentView>
Normally, when you want to binding a value for binableproperty, you could try the code below:
ExpandableView: I make a simple contentview with the same binableproperty for you reference.
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamarinDemo.Pages.ExpandableView">
<ContentView.Content>
<StackLayout>
<Label Text="Hello"></Label>
<Label x:Name="label"></Label>
</StackLayout>
</ContentView.Content>
</ContentView>
Code behind:
public partial class ExpandableView : ContentView
{
public static BindableProperty LabelProperty =
BindableProperty.Create(nameof(Label),
typeof(string),
typeof(ExpandableView),
propertyChanged: (b, o, n) => (b as
ExpandableView).OnLabelChanged());
private void OnLabelChanged()
{
label.Text = Label; //label is the x:Name of your Label control in ExpandableView.xaml
}
public string Label
{
get => (string)GetValue(LabelProperty);
set => SetValue(LabelProperty, value);
}
public ExpandableView()
{
InitializeComponent();
}
}
Usage:
<ContentPage.Content>
<local:ExpandableView Label="{Binding str}"></local:ExpandableView>
</ContentPage.Content>
Code Behind:
public partial class Page1 : ContentPage
{
public string str { get; set; }
public Page1()
{
InitializeComponent();
str = "okay";
this.BindingContext = this;
}
}

How can I bind a XAML element on a template with the C# back end instead of in the XAML?

Here's what I have right now:
I have this template that's simplified for the question:
<?xml version="1.0" encoding="UTF-8"?>
<Frame xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:t="clr-namespace:Japanese.Templates"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.Templates.RoundButtonText" x:Name="this">
<Label Text="ABC"
TextColor="{Binding LabelTextColor, Source={x:Reference this}}"
/>
</Frame>
and this C#
using Xamarin.Forms;
namespace Japanese.Templates
{
public partial class RoundButtonText : BaseFrameButtonTemplate
{
public RoundButtonText()
{
InitializeComponent();
// I would like to put the Label TextColor binding here instead of in the XAML
}
}
}
Can someone help me by telling me how I can add the binding for the label TextColor in the C# back end so that it changes in exactly the same way as it currently does when it's written as:
TextColor="{Binding LabelTextColor, Source={x:Reference this}}"
in XAML?
Remove the binding from the XAML and give the Label control an x:Name:
<?xml version="1.0" encoding="UTF-8"?>
<Frame xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:t="clr-namespace:Japanese.Templates"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.Templates.RoundButtonText" x:Name="this">
<Label x:Name="label"
Text="ABC" />
</Frame>
Set up the binding and the property to bind to in C# code behind:
using Xamarin.Forms;
namespace Japanese.Templates
{
public partial class RoundButtonText : BaseFrameButtonTemplate
{
Color _labelTextColor;
public Color LabelTextColor {
get {
return _labelTextColor;
}
set {
if (_labelTextColor != value) {
_labelTextColor = value;
OnPropertyChanged("LabelTextColor");
}
}
}
public RoundButtonText()
{
InitializeComponent();
label.BindingContext = this;
label.SetBinding(Label.TextColorProperty, "LabelTextColor");
}
}
}
Now whenever the LabelTextColor property value is changed, the Label's TextColor property should change as well, just like with a XAML binding.

How to Create a UserControl using MVVM in Xamarin.Forms

I have tried to develop a usercontrol in Xamarin.Forms but I am not able to bind the controls of that Usercontrol using ViewModel.
My CustomeEntryUserControl is look like
<?xml version="1.0" encoding="UTF-8"?>
<Grid xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="LearningApp.UserControls.CustomeEntry"
xmlns:local="clr-namespace:LearningApp.Extension">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Entry Grid.Column="0" x:Name="entry" />
<local:RoundedButton Grid.Column="1" Text="X" BackgroundColor="Gray" TextColor="White" HeightRequest="20" WidthRequest="10" VerticalOptions="Center" Margin="-30,0,30,0" x:Name="cancelbtn" />
</Grid>
Code behind of this usercontrol is look like
public partial class CustomeEntry : Grid
{
public CustomeEntry()
{
InitializeComponent();
}
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(CustomeEntry), default(string));
public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(CustomeEntry), typeof(ICommand), default(ICommand));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public string PlaceHolder
{
get { return entry.Placeholder; }
set { entry.Placeholder = value; }
}
}
I have tried to use this control in one of my content page is like
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="LearningApp.Views.TriggersDemo.DataTriggers"
xmlns:local="clr-namespace:LearningApp.UserControls">
<StackLayout Orientation="Vertical">
<Label FontSize="Large" FontAttributes="Bold" Text="{Binding lblText}"></Label>
<local:CustomeEntry Text="{Binding Name}" Command="{Binding CancelCommand}" PlaceHolder="Enter Name"/>
<Button Command="{Binding CheckCommand}" Text="Check" />
</StackLayout>
</ContentPage>
code behind of above mentioned content page is
namespace LearningApp.Views.TriggersDemo
{
public partial class DataTriggers : ContentPage
{
public DataTriggers()
{
InitializeComponent();
this.BindingContext = new TriggersViewModel();
}
}
}
and My ViewModel contain the one Property like
private string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; RaisePropertyChanged(); }
}
Can anyone please guide how I can bind the Entry Text of UserControl to my ViewModel property Name direct?
In you CustomerEntry there is no control that displays the Text you're binding to.
You have to set up all the bindings in your custom control too.
You can also pass a handler to the TextProperty's `propertyChanged' parameter, and set other view's properties on behalf.
Remove the properties from the custom control and bind directly to the BindingContext which will be passed along (containing you viewmodel).
Then set Text="{Binding Text=.}" in your custom control's entry (same with Button's Command) to have it bind to the text from the BindingContext, in fact you can remove all your properties from the custom control, and bind directly to the ViewModel.

Resources