Access Method from other Class in Xamarin - c#

I'm stuck with a Xamarin problem. I have a XAML ContentPage file which consists of two ContentView (vm:) in a StackLayout:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Proj1"
xmlns:vm="clr-namespace:Proj1.ViewModels"
x:Class="Proj1.MyMain">
<StackLayout BackgroundColor="{StaticResource MainBG}" Spacing="1">
<vm:DisplayArea />
<vm:ButtonArea />
</StackLayout>
</ContentPage>
The two vm: presents two ContentView areas for labels and buttons. I separated these for simplicity and to keep the XAML files smaller.
So, the general, merged XAML structure looks like this:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Proj1"
xmlns:vm="clr-namespace:Proj1.ViewModels"
x:Class="Proj1.MyMain">
<StackLayout BackgroundColor="{StaticResource MainBG}" Spacing="1">
<ContentView>
...
<Label Grid.Row="0" Grid.Column="1" x:Name="InpRegX" />
...
</ContentView>
<ContentView>
...
<Button ... Clicked="BtnClicked" />
...
</ContentView>
</StackLayout>
</ContentPage>
But I want to have the two ContentView in separate files.
DisplayArea consists among others of a label RegX:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Proj1.ViewModels.DisplayArea">
...
<Label Grid.Row="0" Grid.Column="1" x:Name="InpRegX" />
...
</ContentView>
namespace Proj1.ViewModels
{
public partial class DisplayArea : ContentView
{
public readonly MyClass RegX; // made public for simplicity
public DisplayArea ()
{
InitializeComponent ();
RegX = new MyClass(InpRegX);
}
}
}
Now I want to execute a method .AddChar() of DisplayArea.RegX from a button clock.
namespace Proj1.ViewModels
{
public partial class ButtonArea : ContentView
{
public ButtonArea ()
{
InitializeComponent ();
}
private void BtnClicked(object sender, EventArgs e)
{
var btn = (Button)sender;
DisplayArea.RegX.AddChar(btn.Text); // ERROR!
}
}
}
This creates a compiler error:
An object reference is required for the non-static field, method, or property 'DisplayArea.RegX
This is because I reference RegX via its class, not the real object instance. But how can I find the name the compiler creates for the instance?

The standard in OOP is to create a static class for the utilities with static methods that are global and accessable as you did without creating an instance of the class every time you want to access a variable or a method.
Example:
public static class Util
{
public static string GlobalString = "Hello World";
public static string GetCurrentLanguage()
{
string SelectedLangProp;
if (Application.Current.Properties.ContainsKey("SelectedLangProp"))
{
SelectedLangProp = Application.Current.Properties["SelectedLangProp"] as string;
}
else
{
SelectedLangProp = "AR";//default language
}
return SelectedLangProp;
}
}
You can access static variables from anywhere using:
String TestGlobal = Util.GlobalString; //"Hello World"
Same goes for method calls:
String MethodResult = Util.GetCurrentLanguage();
There is an alternative way which is closer to what you asked which is:
DisplayArea display = new DisplayArea();
String Result = display.RegX.AddChar(btn.Text);
This will work but it will create a new instance of the class which is not recommended especially because you are using a contentview class and doing the logic in the code behind instead of using MVVM is the the recommended structure for building Xamarin apps.

in your XAML, assign a name
<vm:DisplayArea x:Name="MyDisplayArea />
then in your xaml.cs
private void BtnClicked(object sender, EventArgs e)
{
var btn = (Button)sender;
MyDisplayArea.RegX.AddChar(btn.Text); // ERROR!
}

Related

.NET MAUI Binding Issue

Newbie to .NET MAUI and to MVVM. I've seen other examples out there for this, but mine won't work. When I run the code it shows the string PlayProperty in PlayMCanvas as null. I don't know how to get data into the canvas.
VM
public class ShowViewModel
{
public string TheString {get;set; }
public ShowViewModel()
{
TheString = "test";
}
}
View Code Behind
public partial class ShowPlay : ContentPage
{
public ShowViewModel TheVM;
public ShowPlay()
{
InitializeComponent();
TheVM = new ShowViewModel();
TheVM.TheString = "test2";
BindingContext = TheVM;
}
}
XAML View
<?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"
xmlns:drawables="clr-namespace:PlayMApp.Drawables"
xmlns:local="clr-namespace:PlayMApp"
x:Class="PlayMApp.ShowPlay"
x:DataType="local:ShowViewModel"
Title="Show Play">
<VerticalStackLayout>
<Label
Text="Welcome to .NET MAUI!"
VerticalOptions="Center"
HorizontalOptions="Center" />
<Button
Text="Add Motion"
Clicked="AddMotion"
VerticalOptions="Start"
HorizontalOptions="Center"></Button>
<GraphicsView HeightRequest="300"
WidthRequest="400">
<GraphicsView.Drawable>
<drawables:PlayMCanvas Play="{ Binding TheString }" />
</GraphicsView.Drawable>
</GraphicsView>
</VerticalStackLayout>
</ContentPage>
PlayMCanvas
public class PlayMCanvas : GraphicsView, IDrawable
{
public PlayMCanvas()
{
}
public string Play
{
get => (string)GetValue(PlayProperty);
set => SetValue(PlayProperty, value);
}
public static BindableProperty PlayProperty = BindableProperty.Create(nameof(Play), typeof(string), typeof(PlayMCanvas));
public void Draw(ICanvas canvas, RectF dirtyRect)
{
canvas.StrokeColor = Colors.Red;
canvas.StrokeSize = 6;
canvas.DrawLine(10, 10, 90, 100);
canvas.DrawString(Play,40,30,HorizontalAlignment.Left);
}
}
When I get to the final line (DrawString), I think Play should be "test2", but it's null
I've tried making changes to the code to tweak what is sent in. If I send just a plain literal string through the <drawables:PlayMCanvas Play="test" />, it works, but not with the binding
I figured it out, saw another example where someone was very specific in the component they were binding to and followed that.
Added x:Name to the PlayMCanvas like below:
<drawables:PlayMCanvas x:Name="PlayMCan" Play="{Binding TheString}" />
Changed to this in codebehind:
PlayMCan.BindingContext = TheVM;
And now the binding seems to be working.
Thanks for the comments!

Xamarin Effect affecting other controls

I am experiencing a weird bug where a Custom effect would affect other Controls as well while i am only targeting one specific control. I was able to reproduce the behavior in a small test project. What i am trying to achieve is to change the color of a specific entry control. It does work but when navigating back to the previous page the effect affects other controls as well while i only want the specific effect to affect a specific control. I also only add the effect to the specific Entry by using its Effects list from xaml.
(note i am using the standard forms navigation page for navigating defined in my app.xaml like this.
MainPage = new NavigationPage(new MainPage());
MainPage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App2"
x:Class="App2.MainPage">
<StackLayout>
<!-- Place new controls here -->
<Label Text="Welcome to Xamarin.Forms!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Entry></Entry>
<Button Clicked="Button_Clicked" Text="navigate"></Button>
<Button Clicked="Button_Clicked2" Text="navigate to other"></Button>
</StackLayout>
</ContentPage>
CodeBehind of MainPage.xaml
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private void Button_Clicked(object sender, EventArgs e)
{
Application.Current.MainPage.Navigation.PushAsync(new Test());
}
private void Button_Clicked2(object sender, EventArgs e)
{
Application.Current.MainPage.Navigation.PushAsync(new Test2());
}
}
Test.Xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App2"
x:Class="App2.Test">
<ContentPage.Content>
<Entry>
<Entry.Effects>
<local:EntryLineColorEffect></local:EntryLineColorEffect>
</Entry.Effects>
</Entry>
</ContentPage.Content>
</ContentPage>
Test2.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App2.Test2">
<ContentPage.Content>
<StackLayout>
<Label Text="test 2" />
<Entry>
</Entry>
</StackLayout>
</ContentPage.Content>
</ContentPage>
EntryLineColorEffect (forms project)
public class EntryLineColorEffect : RoutingEffect
{
public EntryLineColorEffect() : base("Prolang.EntryLineColorEffect")
{
}
}
EntryLineColorEffect (Android project)
[assembly: ResolutionGroupName("Prolang")]
[assembly: ExportEffect(typeof(EntryLineColorEffect), "EntryLineColorEffect")]
namespace App2.Droid.Effects
{
class EntryLineColorEffect : PlatformEffect
{
EditText control;
protected override void OnAttached()
{
control = Control as EditText;
UpdateLineColor();
}
protected override void OnDetached()
{
control = null;
}
private void UpdateLineColor()
{
if (control != null)
{
control.Background.SetColorFilter(Color.DarkMagenta.ToAndroid(), Android.Graphics.PorterDuff.Mode.SrcAtop);
}
}
}
}
some screenshots that make it more clear
The entry does get the Magenta color which is good
But when navigating back and then navigating to the Test2 page that entry is also getting the Magenta color which for me is undesired behavior (and i do not know why it is hapenning). So what should i do to avoid this behavior?
In the end someone on the Xamarin comunity forum was able to answer my question.
=>
Effect won't affect other controls which haven't attached that effect. It seems Background.SetColorFilter(Color.DarkMagenta.ToAndroid(), Android.Graphics.PorterDuff.Mode.SrcAtop); changes the global Entry's underline's color. The entry displayed later after this setting will apply this behavior. So your Test2's Entry still has an attacted style. Use Background.Mutate().SetColorFilter(Color.DarkMagenta.ToAndroid(), Android.Graphics.PorterDuff.Mode.SrcAtop); instead to correct that:
EditText control;
protected override void OnAttached()
{
control = Control as EditText;
UpdateLineColor();
}
protected override void OnDetached()
{
control = null;
}
private void UpdateLineColor()
{
if (control != null)
{
control.Background.Mutate().SetColorFilter(Color.DarkMagenta.ToAndroid(), Android.Graphics.PorterDuff.Mode.SrcAtop);
}
}

How to bind Listview to Observablecollection in a static class of another namespace

I reed a lot about namespace but still facing a problem / misunderstanding.
I would like to bind a Listview to an Observablecollection which resides in a static class in another namespace. It works but I can't succeed without code. I'm sure there is a better way to do this entirely in the XAML file.
So, this is my XAML file (...a part of - I delete a lot to be clear ...) :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ToolBox"
x:Class="ToolBox.MainPage">
<StackLayout>
<ListView x:Name="listToolBox">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}"
Detail="{Binding CreateDate}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
I set up the binding context of the "listToolBox" in the code behind:
...
using ToolBox.Model;
namespace ToolBox
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
listToolBox.ItemsSource = ToolBox.Model.EBis.ToolBoxList;
}
...
It refers to a member of a static class:
namespace ToolBox.Model
{
public static class EBis
{
public static ObservableCollection<cDocuments> ToolBoxList = new ObservableCollection<cDocuments>
{
new cDocuments{ Id = -1, Name = "Pas (encore) de connection aux serveurs ..."}
};
...
MY QUESTION:
What are the binding instructions I have to write in the XAML elements in order to delete this line of code behind
listToolBox.ItemsSource = ToolBox.Model.EBis.ToolBoxList;
To be complete, the structure of my "Toolbox" APP is here:
Thanks a lot for your advice
Use the x:Static markup extension:
<ListView ItemsSource=“{x:Static local:Model.EBis.ToolBoxList}”>

Set BindingContext to ViewModel in XAML on Xamarin.Forms

I want to develop a simple project with Xamarin.Form and MVVM. In my solution (named XamarinPOC) i have (in addition to standard Xamarin.Forms projects) one separate project for the model (XamarinPOC.Model) and one separate project for the ViewModel (XamarinPOC.ViewModel).
I defined in a XamarinPOC.ViewModel project an abstract class for a BaseViewModel class (that implements the INotifyPropertyChanged Interface) and after I've created a SummaryViewModel class that extend BaseViewModel class with a simple property:
namespace XamarinPOC.ViewModel
{
public class SummaryViewModel : BaseViewModel
{
private string _test = "The binding is OK!";
public String test
{
get
{
return _test;
}
set
{
_test = value;
OnPropertyChanged("test");
}
}
public SummaryViewModel(){}
}
}
Next I created a simple ContentPage (SummatyView) in a XamarinPOC project that contain only a label that i want show the text defined in ViewModel. I want to use a XAML for defining the View and the binding but when I run the app nothing is displayed, I no errors on compile-time and runtime but the text are not displayed. My XAML is this
<?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:local="clr-namespace:XamarinPOC.ViewModel,assembly=XamarinPOC.ViewModel"
x:Class="XamarinPOC.Summary"
Title="Summary List"
BindingContext="XamarinPOC.ViewModel.SummaryViewModel">
<StackLayout>
<Label Text="{Binding test}"/>
</StackLayout>
</ContentPage>
and finally my app.cs is:
namespace XamarinPOC
{
public class App : Application
{
public App()
{
MainPage = new Summary();
}
}
}
In the XamarinPOC project I've added a reference to XamarinPOC.ViewModel and XamarinPOC.Model assemblies.
I think the problem is in the XAML definition of binding, but i don't find the error. Where am I wrong?
To bind the view to the viewmodel from Xaml in your case do it like this
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:XamarinPOC.ViewModel; assembly=XamarinPOC.ViewModel"
x:Class="XamarinPOC.Summary"
Title="Summary List">
<ContentPage.BindingContext>
<viewModels:SummaryViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Label Text="{Binding test}"/>
</StackLayout>
</ContentPage>
One side note I noticed is with naming conventions, it is better to put all your ViewModels, even if it is only one viewModel, inside a folder named "ViewModels" So the namespace in your case would be XamarinPOC.ViewModels

Xamarin.Forms accessing controls written in markup from Code

Im trying to add some items to a Listview which i added using Xamarin.Forms markup in an xaml file.
The button can be accessed by hooking with the click event.But since the listview is empty i need the event like ondraw like in winforms, so that i can hook to it when it is drawn.
In the XAML file I have :
<?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="ButtonXaml.ButtonXamlPage">
<StackLayout>
<Button Text="Tap for click count!"
BorderWidth="10"
TextColor="Red"
HorizontalOptions="Center"
Clicked="OnButtonClicked" />
<ListView
HorizontalOptions="Center"
/>
</StackLayout>
</ContentPage>
In the .cs file i have
using System;
using Xamarin.Forms;
namespace ButtonXaml
{
public partial class ButtonXamlPage
{
int count = 0;
public ButtonXamlPage()
{
InitializeComponent();
}
public void OnButtonClicked(object sender, EventArgs args)
{
((Button)sender).Text = "You clicked me";
}
}
}
So should i hook to events in Listview or can i do something like Resource.getElementbyID like we do in android
To access a Forms control in the code-behind, you need to assign it a name, using the x:Name attribute
in XAML:
<ListView HorizontalOptions="Center" x:Name="MyList" />
in code:
MyList.ItemsSource = myData;
There is a bug in Xamarin where VS doesn't see the defined x:Name
http://forums.xamarin.com/discussion/25409/problem-with-xaml-x-name-and-access-from-code-behind
Say you've defined an image in XAML:
<Image x:Name="myImageXName" />
Then this should work in code behind:
this.FindByName<Image>("myImageXName");
In my case the problem was lack of line XamlCompilation(XamlCompilationOptions.Compile)] in .xaml.cs file.
Example:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new MainPageViewModel();
}
...
}

Categories