1

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?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Thomas Z
  • 89
  • 1
  • 1
  • 9
  • 1
    Possible duplicate of [Accessing a property in one ViewModel from another](https://stackoverflow.com/questions/16506653/accessing-a-property-in-one-viewmodel-from-another) – user247702 Oct 12 '18 at 20:16
  • Yes, this is somehow the same issue. Thanks Stijn. – Thomas Z Oct 14 '18 at 14:33

2 Answers2

0

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.

Ali123
  • 740
  • 13
  • 38
  • I know the concept of static variables. But I think this will not work here. Visual Studio auto-creates DisplayArea as a 'public *partial* class'. I cannot change this to static, because this will create a lot of other errors. (I enhanced my initial question with some additional information) – Thomas Z Oct 15 '18 at 06:55
0

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!
    }
Jason
  • 86,222
  • 15
  • 131
  • 146
  • Hi Jason, this would be a cool solution, but seems not to work, because vm:DisplayArea is not known elsewhere. I tried 'x:Name="MyDisplayArea"' to name that ContentArea, but then the error messages states **The name 'MyDisplayArea' does not exists in the current context.** – Thomas Z Oct 14 '18 at 11:27
  • looking at your code again, you appear to be trying to modify the parent ContentPage the codebehind of the ContentArea. This won't work. You either need to expose an event handler in ContentArea that the page can respond to, or use MessagingCenter – Jason Oct 14 '18 at 14:08