Dynamically add Icons to titlebar Maui - icons

Im creating an App where I want have multiple icons in my titlebar (they should be clickable). One Icon should be on the Left of the title text like so (this icon is static and will not change):
The other Icons should be on the right (these icons should be dynamic, as they will be used to alert the user if anything important happens):
I'm using a simple shell, so no Navigationpage, thus I don't think <NavigationPage.Titleview> can be used in this case. Is there any way to achieve something like this in Maui?

You can use the shell title view as stated in this answer, it is pointing to Xamarin documentation but it is also valid for MAUI.
In your case you will have something like this inside your AppShell.xaml :
<Shell.TitleView>
<Grid ColumnDefinitions="auto,*,auto">
<Label Text="Title" BackgroundColor="Red"></Label>
<HorizontalStackLayout Grid.Column="2">
<Label Text="Icon" BackgroundColor="Red"></Label>
</HorizontalStackLayout>
</Grid>
</Shell.TitleView>
Which look like this (very trendy design) :
How to update the icons can be done by data binding or creating directly the object from C# :
<HorizontalStackLayout Grid.Column="2" x:Name="IconsContainer">
<Label Text="Icon" BackgroundColor="Red" IsVisible="{Binding Icon1Visible}"></Label>
</HorizontalStackLayout>
And in the C# :
public partial class AppShell : Shell
{
public bool Icon1Visible { get; set; } = false;
public AppShell()
{
InitializeComponent();
// An object containing the binding data
BindingContext = this;
// Directly create a new icon
AddIcon();
// Show an icon through binding
ShowIcon();
}
public void ShowIcon()
{
Icon1Visible = true;
}
public void AddIcon()
{
var icon = new Label();
icon.Text = "And another one";
IconsContainer.Children.Add(icon);
}
}
To get whenever you want to update the icons I would go with dependency injection and either pass directly a view model to bind the icons on or using events.

Related

How to change property value of a view inside CarouselView's ItemTemplate?

I have a CarouselView with ItemTemplate containing a Label and a Grid. Outside of that CarouselView, I want to make a button to modify Carousel's current item's Grid's visibility. Because it's inside ItemTemplate, I can't use x:Name to refer to that specific Grid, so how can I refer to the current item's Grid so I can change its property value? Thank you.
You will want to do that through databinding. As you already mentioned, you can't use x:Name. This is because you're inside of a template. The value in x:Name would be duplicated for each time that template is applied to a concrete item in your list, in this case a CarouselView. Moreover; if you use virtualization for that list, a template might not even exist at all at that point in time. All reasons why you can't use x:Name to reference anything inside of a template.
I don't have any info about the code you want to use this with, so I'll make something up.
If the backing collection for your CarouselView is a ObservableCollection<MyItem>, then your CarouselView might look something like this:
<!-- Databinding scope here is MyViewModel -->
<CarouselView ItemsSource="{Binding MyItemsCollection}">
<CarouselView.ItemTemplate>
<!-- Databinding scope here is MyItem -->
<DataTemplate>
<Button Text="Delete" IsVisible="{Binding CanDelete}" />
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
So you will have a backing view model which has a MyItemsCollection, and your page (that holds the CarouselView) has set the BindingContext to a new instance of MyViewModel:
public class MyViewModel
{
public ObservableCollection<MyItem> MyItemsCollection { get; set; } = new ObservableCollection<MyItem>();
private void LoadData()
{
var items = _myItemService.GetItems();
foreach (var item in items)
{
MyItemsCollection.Add(item);
}
}
}
Whenever you want to influence the IsVisible you will want to set the CanDelete of the MyItem that it's about to false. Let's assume MyItem looks like this:
public class MyItem
{
public bool CanDelete { get; set; }
}
You will need to implement the INotifyPropertyChanged interface on it so that the UI will pick up on any changes that are made to property values.
Now whenever you set the CanDelete of a certain instance of MyItem to false, that will change your UI. E.g.: MyItemsCollection[3].CanDelete = false;
On my YouTube channel I added a playlist with videos about data binding which might help in cases like these.
PS. At the time of writing IsVisible is bugged in .NET MAUI

Xamarin - get control dimensions

I really don't know how to get control dimensions in Xamarin Forms. I thought it would be as easy as:
View control = GetControlSomehow();
var bounds = control.Bounds;
But View.Bounds returns different things depending on wether the control is inside a grid or not; or wether the main layout is AbsoluteLayout or some other one.
For example if I do something like:
<AbsoluteLayout>
<Button AbsoluteLayout.LayoutBounds="200,200" x:Name="btn" Text="Btn"/>
</AbsoluteLayout>
then I am not able to read actual height or width of that button. Although MSDN says that Height & Width properties should return these values.
I tried even:
var width = ctrl.Width;
or
var width = ctrl.GetValue(WidthProperty);
But really non of these worked. I always get -1
So how can I get the control dimensions that could be reliable?
Complete example showing how to pass button itself, using data binding.
AbsoluteLayoutPage.xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestBugs.AbsoluteLayoutPage">
<ContentPage.Content>
<AbsoluteLayout>
<Button AbsoluteLayout.LayoutBounds="200,200" x:Name="btn" Text="Btn"
Command="{Binding ButtonCommand}" CommandParameter="{x:Reference btn}"/>
</AbsoluteLayout>
</ContentPage.Content>
</ContentPage>
AbsoluteLayoutPage.xaml.cs:
using System.Diagnostics;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TestBugs
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class AbsoluteLayoutPage : ContentPage
{
public AbsoluteLayoutPage()
{
ButtonCommand = new Command(ButtonClick);
InitializeComponent();
BindingContext = this;
}
private void ButtonClick(object ob)
{
var button = ob as View;
var bounds = btn.Bounds;
var width = btn.Width;
var height = btn.Height;
Debug.WriteLine($"--- ButtonClick {bounds}, ({width},{height})---");
}
public Command ButtonCommand { get; set; }
}
}
When click button, in VS Output pane:
[0:] --- ButtonClick {X=200 Y=200 Width=88 Height=48}, (88,48)---
this works for me
<Button Text="Click" Clicked="Clicked" />
public void Clicked(object sender, EventArgs a)
{
var btn = (Button)sender;
var w = btn.Width;
var h = btn.Height;
DisplayAlert("Dimesions", $"{w}x{h}","OK");
}
Sorry guys, my bad.
I was testing it on two pages - the real one and the test one. The test page is built in a different way and I confirm that the problem was in time when I retrieved these values. Control wasn't painted yet.
So I am really sorry to bother you and thank you all for investing your time into this.

Xamarin Forms - Share Screen, can you share a "hyperlink"?

I'm learning Xamarin Forms. And specifically, if I want to share a complex link, but have the text be something simple how do you do that? What I have now is below.
await Share.RequestAsync(new ShareTextRequest
{
Uri = "thelink",
Title = "title", //ios doesn't use title
Text = "extra text"
});
That obviously just puts in the link and some text. But I want something more akin to an html hyperlink.
Well if you are asking how to put a hiperlink in Xamarin forms, you can do it like this, just create a class in the Portable project and then call it from your XAML page, but remember to read the Xamarin Essentials documentation cause you need to put this in you info.plist:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>lyft</string>
<string>fb</string>
</array>
If you mean the Label with Hyperlinks , you can have a look at this doc here .
The text displayed by Label and Span instances can be turned into hyperlinks, sample code as follow :
<Label>
<Label.FormattedText>
<FormattedString>
<Span Text="Alternatively, click " />
<Span Text="here"
TextColor="Blue"
TextDecorations="Underline">
<Span.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapCommand}"
CommandParameter="https://learn.microsoft.com/xamarin/" />
</Span.GestureRecognizers>
</Span>
<Span Text=" to view Xamarin documentation." />
</FormattedString>
</Label.FormattedText>
</Label>
When the hyperlink is tapped, the TapGestureRecognizer will respond by executing the ICommand defined by its Command property. In addition, the URL specified by the CommandParameter property will be passed to the ICommand as a parameter.
The code-behind for the XAML page contains the TapCommand implementation:
public partial class MainPage : ContentPage
{
// Launcher.OpenAsync is provided by Xamarin.Essentials.
public ICommand TapCommand => new Command<string>(async (url) => await Launcher.OpenAsync(url));
public MainPage()
{
InitializeComponent();
BindingContext = this;
}
}
The effect :
In addition , you also can create a reusable hyperlink class to make the xaml code more sample .

Unable to select text in Xamarin.Forms Editor control or custom Borderless Editor using renderer

I've implemented Borderless Editor in my Xamarin.Forms application using Custom EditorRenderer. But I'm facing an issue that the editor text is not selectable at all in both cases, neither in Forms editor control nor in Rendered native editor control. My app has the functionality to let user copy paste the text in the editor while typing, like in any other text editing app. This is a basic feature in most of the apps and is by default there. But it's not working in my app. I've tried enabling it through
Control.SetTextIsSelectable(true);
but still it's not working. I've tried other things too, like:
Control.CustomSelectionActionModeCallback = new
CustomSelectionActionModeCallback();
Control.CustomInsertionActionModeCallback = new CustomInsertionActionModeCallback();
But nothing is working at all and text is not getting selected even a single word. Does anyone has any idea about this issue? How can I make the text selectable and allow default copy paste feature in custom editor?
Here's my code in Xaml:
<renderer:BorderlessEditor
Grid.Row="1"
x:Name="UserTextEditorAndroid"
BackgroundColor="{StaticResource WhiteColor}"
HeightRequest="350"
Margin="20,2"
MaxLength="1024"
IsReadOnly="{Binding Source={x:Reference LongTextTemplate}, Path=Editable, Converter={StaticResource InverseBool}}" />
And the custom render code is:
public class BorderlessEditorRenderer : EditorRenderer
{
public BorderlessEditorRenderer()
{
}
public static void Init() { }
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
if (e.OldElement == null)
{
Control.Background = null;
var layoutParams = new MarginLayoutParams(Control.LayoutParameters);
layoutParams.SetMargins(0, 0, 0, 0);
LayoutParameters = layoutParams;
Control.LayoutParameters = layoutParams;
Control.SetPadding(0, 0, 0, 0);
SetPadding(0, 0, 0, 0);
Control.SetTextIsSelectable(true);
Control.VerticalScrollBarEnabled = false;
}
}
}
Even if I use Xamarin.Forms own editor in Xaml instead of custom renderer then also it doesn't work at all. The text is still un-selectable.
I am having the same problem - but I'm not using Telerik. I'm running VS 2019 on Win 10. This is easy to repro ...
Create a new "Mobile App (Xamarin Forms)" and simply add an Editor to the MainPage.xaml file. I also put the Editor inside a ScrollView for kicks. The code below only shows a few words of text placed in the Editor, but I actually used a long paragraph from the Microsoft docs. I left that out below so as to simplify code presentation ... and avoid a mess!
<?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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="Editor_Test.MainPage">
<StackLayout>
<!-- Place new controls here -->
<ScrollView>
<Editor Text="Visual Studio makes it easier ..."
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" />
</ScrollView>
</StackLayout>
</ContentPage>
I get normal behavior:
Because of the ScrollView, I can scroll the text with a finger (mouse) drag
A long press (click) selects the word pressed
A double tap (click) selects the word tapped
I get different results if I change 1 line of code in the App ctor, in App.xaml.cs, to instantiate the MainPage with a NavigationPage, like so:
public App()
{
InitializeComponent();
//MainPage = new MainPage();
MainPage = new NavigationPage(new MainPage());
}
Now the behavior changes:
A long press (click) has no effect (no selection)
A double tap (click) has no effect (no selection)
In fact there is no way at all that I can see to select text in the Editor. I don't know why this happens ... but it does. It drove me a bit mad until I realized what was causing it.
I'm using:
Xamarin Forms 4.5.0.495
Xamarin Essentials 1.3.1
Building for Android 9 (Pie)
I'm new to Android/Xamarin ... so my question is, how best to implement my own simple page navigation - ie, without using Xamarin's NavigationPage? I have a simple 3 page app I'd like to make some progress with!

XF binding data from a popuppage to a contentpage through xaml

I have a popup page in my application which includes some checkboxes, and I also have a ContentPage in my MainPage which contains some stacklayouts, I want to bind the IsVisible Property of my stacklayout to the IsChecked property of my checkbox (which is in another page), but i don't know how to do it, can anybody please help?
I've tried this, but it doesn't work
here's the code in my popup page:
<StackLayout Orientation="Horizontal" Spacing="60">
<local:Checkbox x:Name="va1Checkbox" Text="VA1"/>
<local:Checkbox x:Name="va2Checkbox" Text="VA2"/>
</StackLayout>
and this is the piece of code i have in my MainPage:
<StackLayout IsVisible="{Binding Source={x:Reference
va1Checkbox},Path=IsChecked}">
</StackLayout>
Thanks in advance
<StackLayout Orientation="Horizontal" Spacing="60">
<local:Checkbox x:Name="va1Checkbox" Text="VA1"/>
<local:Checkbox x:Name="va2Checkbox" Text="VA2"/>
</StackLayout>
I believe your popup represents some kind of settings based on which you control your StackLayout in Main Page.
If this is the case, there are two possible solutions.
Create a separate class which is binding for both checkbox and layout. You can use this either with MVVM or without it.
Value Passing: open popup from Main Page and register to its close event. When popup is closed you can use its checkbox's value to enable/disable the layout.
In your Popup.xaml.cs :
create an event action
public event Action<Popup> OnClose;
I believe you will have a way to close it. I don't know you are closing it so I will just use OnBackButtonPressed() here:
protected override bool OnBackButtonPressed()
{
OnClose?.Invoke(this);
return base.OnBackButtonPressed();
}
In your MainPage.xaml.cs :
private void OpenPopup()
{
var popup = new PopupPage();
popup.OnClose += OnPopupClosed;
}
void OnPopupClosed(Popup popup)
{
yourStackLayout.IsVisible = popup.va1Checkbox.Value;
}
UPDATE:
You can pass custom data class instead of popup object:
public class PopupData
{
public bool va1CheckboxValue;
public bool va1CheckboxValue;
// other data which you need to access in other page.
}
then
public event Action<PopupData> OnClose;
and
protected override bool OnBackButtonPressed()
{
PopupData data = new PopupData() { va1CheckboxValue = va1Checkbox.Value; }
OnClose?.Invoke(data);
return base.OnBackButtonPressed();
}
and in main page:
void OnPopupClosed(PopupData data)
{
yourStackLayout.IsVisible = data.va1CheckboxValue;
}
Hope this helps.
I don't know very well XAML and MVVM, but I think you can pass MainPage's ViewModel to Popup... if you change MainPage's ViewModel properties in Popup I think these changes will reflect also to MainPage's binded controls.
If you are not using MVVM (you should...), I think you have to pass a reference to properties used in MainPage to Popup... in this way, Popup can change MainPage's properties and its UI could change

Resources