来自ViewModel的标签文本绑定未生效

huangapple go评论89阅读模式
英文:

Binding of label text from ViewModel not working

问题

我正在学习.NET MAUI,目前遇到了数据绑定的问题。我已经阅读了Microsoft文档上关于数据绑定的部分,但我仍然不明白为什么它不起作用。

我可以显示按钮和第二个标签。但是一旦我将其绑定到视图模型上的属性,它就不显示了。如果我使用Binding,它会显示MauiTestApp.ViewModel.MainViewModel。我从中可以得出数据上下文是正确的,并且我应该能够访问属性。

这是我的视图模型:

using MauiTestApp.Model;
using System.Collections.ObjectModel;

namespace MauiTestApp.ViewModel
{
	public partial class MainViewModel : ObservableObject
	{
		public ObservableCollection<Monkey> Monkeys = new ObservableCollection<Monkey>();
		public String s = "Hello world";
		public MainViewModel()
		{
			Monkeys.Add(new Monkey("Bob", 20));
			Monkeys.Add(new Monkey("Steve", 10));
			Monkeys.Add(new Monkey("Joey", 14));
		}
	
		public void AddMonkey()
		{
			Monkeys.Add(new Monkey("Harry", 23));
		}
	}
}

这是XAML:

<?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:local="clr-namespace:MauiTestApp"
             xmlns:viewModel="clr-namespace:MauiTestApp.ViewModel"
             xmlns:model="clr-namespace:MauiTestApp.Model"
			 x:Class="MauiTestApp.MainPage">
	<ContentPage.BindingContext>
		<viewModel:MainViewModel/>
	</ContentPage.BindingContext>
	
	<ScrollView>
		<Grid>
			<Grid.RowDefinitions>
				<RowDefinition Height="50"></RowDefinition>
				<RowDefinition Height="30"></RowDefinition>
				<RowDefinition Height="30"></RowDefinition>
			</Grid.RowDefinitions>
			<Button Grid.Row="0" Text="Press me" Pressed="Button_Pressed"></Button>
			<Label Grid.Row="1" Text="{Binding s}" TextColor="Purple"></Label>
			<Label Grid.Row="2" Text="Hello world" TextColor="Purple"></Label>
		</Grid>
	</ScrollView>
</ContentPage>

以及XAML代码的后台:

namespace MauiTestApp
{
	public partial class MainPage : ContentPage
	{
		public MainViewModel viewModel;
		public MainPage(MainViewModel vm)
		{
			InitializeComponent();
			viewModel = vm;
		}

		private void Button_Pressed(object sender, EventArgs e)
		{
			 viewModel.AddMonkey();
		}
	}
}

如果您需要进一步的帮助,请告诉我。

英文:

I am learning .NET MAUI at the moment, and I have run into an issue with data binding. I have read the section on data binding on the Microsoft docs, but I am still none the wiser of why it isn't working.

I can display the button and the second label. However as soon as I bind it to a property on the view model it does not display. If I use the Binding . it displays the MauiTestApp.ViewModel.MainViewModel. What I can glean from that is that the Data context is correct, and that I should just be able to access the property's.

Here is my View Model:

using MauiTestApp.Model;
using System.Collections.ObjectModel;

namespace MauiTestApp.ViewModel
{
	public partial class MainViewModel : ObservableObject
	{
		public ObservableCollection&lt;Monkey&gt; Monkeys = new ObservableCollection&lt;Monkey&gt;();
		public String s = &quot;Hello world&quot;;
		public MainViewModel()
		{
			Monkeys.Add(new Monkey(&quot;Bob&quot;, 20));
			Monkeys.Add(new Monkey(&quot;Steve&quot;, 10));
			Monkeys.Add(new Monkey(&quot;Joey&quot;, 14));
		}
	
		public void AddMonkey()
		{
			Monkeys.Add(new Monkey(&quot;Harry&quot;, 23));
		}
	}
}

And here is the XAML

&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
&lt;ContentPage xmlns=&quot;http://schemas.microsoft.com/dotnet/2021/maui&quot;
             xmlns:x=&quot;http://schemas.microsoft.com/winfx/2009/xaml&quot;
             xmlns:local=&quot;clr-namespace:MauiTestApp&quot;
             xmlns:viewModel=&quot;clr-namespace:MauiTestApp.ViewModel&quot;
             xmlns:model=&quot;clr-namespace:MauiTestApp.Model&quot;
			 x:Class=&quot;MauiTestApp.MainPage&quot;&gt;
	&lt;ContentPage.BindingContext&gt;
		&lt;viewModel:MainViewModel/&gt;
	&lt;/ContentPage.BindingContext&gt;
	
	&lt;ScrollView&gt;
		&lt;Grid&gt;
			&lt;Grid.RowDefinitions&gt;
				&lt;RowDefinition Height=&quot;50&quot;&gt;&lt;/RowDefinition&gt;
				&lt;RowDefinition Height=&quot;30&quot;&gt;&lt;/RowDefinition&gt;
				&lt;RowDefinition Height=&quot;30&quot;&gt;&lt;/RowDefinition&gt;
			&lt;/Grid.RowDefinitions&gt;
			&lt;Button Grid.Row=&quot;0&quot; Text=&quot;Press me&quot; Pressed=&quot;Button_Pressed&quot;&gt;&lt;/Button&gt;
			&lt;Label Grid.Row=&quot;1&quot; Text=&quot;{Binding s}&quot; TextColor=&quot;Purple&quot; &gt;&lt;/Label&gt;
			&lt;Label Grid.Row=&quot;2&quot; Text=&quot;Hello world&quot; TextColor=&quot;Purple&quot;&gt;&lt;/Label&gt;
    &lt;/ScrollView&gt;

&lt;/ContentPage&gt;

And the XAML code behind:

namespace MauiTestApp
{
	public partial class MainPage : ContentPage
	{
		public MainViewModel viewModel;
		public MainPage(MainViewModel vm)
		{
			InitializeComponent();
			viewModel = vm;
		}

		private void Button_Pressed(object sender, EventArgs e)
		{
			 viewModel.AddMonkey();
		}
	}
}

I'm sorry if it is a basic question, but I am completely stuck at this point.

Thanks for the help!!!

答案1

得分: 1

你只能绑定到公共属性

这不是一个属性

public String s = "Hello world";

这是

public String s { get; } = "Hello world";
英文:

You can only bind to public properties

this is not a property

public String s = &quot;Hello world&quot;;

this is

public String s { get; } = &quot;Hello world&quot;;

答案2

得分: 1

以下是您要翻译的内容:

"The reason you don't see any Text in the Label is because it is not a property. You need to use MVVM Toolkit to make your life easier. Read more here

So your binding in the ViewModel should look like this

[ObservableProperty]
private string s = "Hello world";

Not that it is a private field with a small s. With the ObservableProperty, it is generated into a proper Property that is public and assigned using capital S. Important to always do this. Never use the private field. Always the property.

Now there are a lot of problems with your code in general so if you don't mind I will give you a possible solution to what I could translate from your code. I hope it helps.

Firstly, your complete viewmodel can look like this. Notice the Method with the [RelayCommand]. It will take your method and make it into a bindable command. Also it follows a naming convention. So it must never end with command. To bind to it we add Command in the view. By doing this we decouple our viewmodel from all reference to MAUI references.

public partial class MonkeyViewModel : ObservableObject
{
    [ObservableProperty]
    private ObservableCollection<Monkey> _monkeys;

    [ObservableProperty]
    private string s = "Hello world";

    public MonkeyViewModel()
    {
        Monkeys = new()
        {
            new Monkey
            {
                Name = "Bob",
                Location = 20
            },
            new Monkey
            {
                Name = "Steve",
                Location = 10
            },
            new Monkey
            {
                Name = "Joey",
                Location = 14
            },
        };
    }

    [RelayCommand]
    public void AddMonkey()
    {
        var harryMonkey = new Monkey
        {
            Name = "Harry",
            Location = 23
        };

        Monkeys.Add(harryMonkey);
    }
}

If we look at your view. It could look like this, showing a list of monkeys and your buttons.

<VerticalStackLayout>
    <CollectionView EmptyView = "No data" ItemsSource="{Binding Monkeys}">
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="models:Monkey">
                <Grid Padding = "10">
                    <Grid.RowDefinitions>
                        <RowDefinition Height = "*" />
                        <RowDefinition Height = "*" />
                        <RowDefinition Height = "Auto" />
                    </Grid.RowDefinitions>
                    <Label Grid.Row="0" Text="{Binding Name}" />
                    <Label Grid.Row="1" Text= "{Binding Location}" />
                    <BoxView
                        Grid.Row= "2"
                        BackgroundColor= "Gray"
                        HeightRequest= "1" />
                </Grid>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
    <Button
        Command= "{Binding AddMonkeyCommand}"
        Text= "Press me" />
    <Label
        Text= "{Binding S}"
        TextColor= "Purple" />
</VerticalStackLayout>

Code Behind. Never forget to bind to your BindingContext, either in here or like you did, in the xaml. If not your view will not bind to anything.

public partial class MainPage : ContentPage
{
    private readonly MonkeyViewModel _monkeyViewModel;
    public MainPage(MonkeyViewModel monkeyViewModel)
    {
        _monkeyViewModel = monkeyViewModel;
        InitializeComponent();
        BindingContext = _monkeyViewModel;
    }
}
英文:

The reason you don't see any Text in the Label is because it is not a property. You need to use MVVM Toolkit to make your life easier. Read more here

So your binding in the ViewModel should look like this

[ObservableProperty]
private string s = &quot;Hello world&quot;;

Not that it is a private field with a small s. With the ObservableProperty it is generated into a proper Property that is public and assigned using capital S. Important to always do this. Never use the private field. Always the property.

Now there are a lot of problems with your code in general so if you don't mind I will give you a possible solution to what I could translate from your code. I hope it helps.

Firstly, your complete viewmodel can look like this. Notice the Method with the [RelayCommand]. It will take your method and make it into a bindable command. Also it follow a naming convention. So it must never end with command. To bind to it we add Command in the view. By doing this we decouple our viewmodel from all reference to MUAI references.

public partial class MonkeyViewModel : ObservableObject
{
    [ObservableProperty]
    private ObservableCollection&lt;Monkey&gt; _monkeys;

    [ObservableProperty]
    private string s = &quot;Hello world&quot;;

    public MonkeyViewModel()
    {
        Monkeys = new()
        {
            new Monkey
            {
                Name = &quot;Bob&quot;,
                Location = 20
            },
            new Monkey
                              {
                Name = &quot;Steve&quot;,
                Location = 10
                },
            new Monkey
            {
                Name = &quot;Joey&quot;,
                Location = 14
            },
        };
    }

    [RelayCommand]
    public void AddMonkey()
    {
        var harryMonkey = new Monkey
        {
            Name = &quot;Harry&quot;,
            Location = 23
        };

        Monkeys.Add(harryMonkey);
    }
}

If we look at your view. It could look like this, showing a list of moneys and your buttons.

&lt;VerticalStackLayout&gt;
    &lt;CollectionView EmptyView = &quot;No data&quot; ItemsSource=&quot;{Binding Monkeys}&quot;&gt;
        &lt;CollectionView.ItemTemplate&gt;
            &lt;DataTemplate x:DataType=&quot;models:Monkey&quot;&gt;
                &lt;Grid Padding = &quot;10&quot;&gt;
                    &lt;Grid.RowDefinitions&gt;
                        &lt;RowDefinition Height = &quot;*&quot; /&gt;
                        &lt;RowDefinition Height = &quot;*&quot; /&gt;
                        &lt;RowDefinition Height = &quot;Auto&quot; /&gt;
                    &lt;/Grid.RowDefinitions&gt;
                    &lt;Label Grid.Row=&quot;0&quot; Text=&quot;{Binding Name}&quot; /&gt;
                    &lt;Label Grid.Row=&quot;1&quot; Text= &quot;{Binding Location}&quot; /&gt;
                    &lt;BoxView
                        Grid.Row= &quot;2&quot;
                        BackgroundColor= &quot;Gray&quot;
                        HeightRequest= &quot;1&quot; /&gt;
                &lt;/Grid&gt;
            &lt;/DataTemplate&gt;
        &lt;/CollectionView.ItemTemplate&gt;
    &lt;/CollectionView&gt;
    &lt;Button
        Command= &quot;{Binding AddMonkeyCommand}&quot;
        Text= &quot;Press me&quot; /&gt;
    &lt;Label
        Text= &quot;{Binding S}&quot;
        TextColor= &quot;Purple&quot; /&gt;

&lt;/VerticalStackLayout&gt;

Code Behind. Never forget to bind to your BindingContext, either in here or like you did, in the xaml. If not your view will not bind to anything.

public partial class MainPage : ContentPage
{
    private readonly MonkeyViewModel _monkeyViewModel;
    public MainPage(MonkeyViewModel monkeyViewModel)
    {
        _monkeyViewModel = monkeyViewModel;
        InitializeComponent();
        BindingContext = _monkeyViewModel;
    }
}

huangapple
  • 本文由 发表于 2023年8月5日 11:39:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/76840057.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定