将变量传递到视图模型时数据绑定失败

问题描述 投票:0回答:1

我有一个带有名称选择器、数据选择器、按钮和一些标签的用户界面,按下按钮时,我使用选定的名称从数据库中获取一堆数据。然后我想使用视图模型来计算数字并通过将变量绑定到我的 UI 返回日出和日落时间。

使用断点,我可以看到一切正常,标签绑定会立即获取文本,直到我继续调试。我试过:

  1. 在过程括号内传递变量

  2. 在虚拟机中设置绑定变量并设置 xaml.cs 中的值

日出.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"
             x:Class="MrDiver.Views.Sunrise"
             BackgroundColor="LightYellow"
             Title="Sunrise/Sunset"
             xmlns:vm="clr-namespace:MrDiver.ViewModel">
    
    <ContentPage.BindingContext>
        <vm:SunViewModel />
    </ContentPage.BindingContext>

    <VerticalStackLayout
    Spacing="10"
    Margin="20"
    HorizontalOptions="Center"
    BackgroundColor="LightCoral"
    >

        <Label 
        Scale="0.9"
        Text="Sunrise / Sunset CALCULATOR"
        FontSize="Large"
        TextColor="White"
        FontAttributes="Bold"
        VerticalOptions="Center" 
        HorizontalOptions="Center" 
        />

        <Picker 
        Scale="0.9"
        BackgroundColor="LightGray" 
        Title="Select town and date" 
        TitleColor="White" 
        FontAttributes="Italic" 
        TextColor="Black" 
        FontSize="Medium" 
        x:Name="myTownPickerView"/>

        <DatePicker 
        HorizontalOptions="Center"
        Scale="0.9"
        BackgroundColor="LightGray" 
        FontAttributes="Italic" 
        TextColor="Black" 
        FontAutoScalingEnabled="True" 
        x:Name="TownDate"/>

        <Button 
        Scale="0.9"
        BorderWidth="5"
        Text="Submit" 
        FontSize="Title" 
        FontAttributes="Bold" 
        x:Name="TownSubmit"  
        Clicked="OnButtonClicked" />

        <Label 
        HorizontalOptions="Center"
        Text="MY Label" 
        x:Name="mylabel"></Label>

        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            
            <Label Grid.Row="0" Grid.Column="0" HorizontalOptions="Center" Text="Sunrise "></Label>
            <Label Grid.Row="0" Grid.Column="1" HorizontalOptions="Center" Text="{Binding MySunriseBinding}"></Label>
            <Label Grid.Row="1" Grid.Column="0" HorizontalOptions="Center" Text="Sunset "></Label>
            <Label Grid.Row="1" Grid.Column="1" HorizontalOptions="Center" Text="{Binding MySunsetBinding}"></Label>

        </Grid>
    </VerticalStackLayout>
</ContentPage>

日出.xaml.cs

 async void OnButtonClicked(object sender, EventArgs args)
    {
        // Does a fancy rotate
        await mylabel.RelRotateTo(360, 1000);

        //Get the Town detail from the table
        var townID = 1 + (Int32.Parse(myTownPickerView.SelectedIndex.ToString()));
        PortDatabase portDatabase = new PortDatabase();
        var listatown = portDatabase.TownName(townID).Result;

        // Gets the town name, Lon and Lat into variables
        int selectedIndex = myTownPickerView.SelectedIndex;
        String townName = myTownPickerView.Items[selectedIndex];
        Double townLat = (double)listatown[0].Latitude;
        Double townLong = (double)listatown[0].Longitude;
        int zeneth = 91;
        int daylightSaving = 0;

        // Gets the selected date
        DateTime selectDate = TownDate.Date;

        // Displays the name, latitude and longitude
        mylabel.Text = townName + "  Lat:" + townLat + "  Long:" + townLong;

        //Get the surise sunset time
        SunViewModel sunViewModel = new SunViewModel();
        sunViewModel.zenith = 90;
        sunViewModel.Todaydate = selectDate;
        sunViewModel.Longdeg = townLong;
        sunViewModel.Latdeg = townLat;
        sunViewModel.SunriseModel();


}

SunViewModel.cs


public class SunViewModel : ContentView
{
   
    public string MySunriseBinding { get; set; }
    public string MySunsetBinding { get; set; }
    public DateTime Todaydate { get; set; }
    public Double Longdeg { get; set; }
    public Double Latdeg { get; set; }
    public int zenith {  get; set; }
    public int daylightsaving { get; set; }



    public double PI_BY_180 = 0.017453293;
    public SunViewModel()
    {
        MySunriseBinding = "";
        MySunsetBinding = "";

    }

    public void SunriseModel()
    {
        // a mess of calculations finishing with...

        MySunriseBinding = MySunrise;
        MySunsetBinding = MySunset;

    }
}
c# .net data-binding viewmodel
1个回答
0
投票

为了重现您描述的行为,我将您的代码作为 .NET MAUI 应用程序运行(即使它没有这样标记)。据我了解,单击[提交]按钮应该执行一些计算并最终更新日出和日落的值。例如。

仔细阅读你的代码,似乎对

SunViewModel
的实例化可能存在一些误解。您似乎走在正确的轨道上,xaml 文件中的此块正确实例化了
SunViewModel
对象并将其设置为视图的绑定上下文。

<ContentPage.BindingContext>
    <vm:SunViewModel />
</ContentPage.BindingContext>

您可以通过将此行添加到您的

MainPage
构造函数中来证明它正常工作

public partial class MainPage : ContentPage
{        
    public MainPage()
    {
        InitializeComponent();

        // This shows that the binding context is already set.
        Debug.Assert(
            base.BindingContext is MrDiver.ViewModel.SunViewModel,
            "Expecting BindingContext has already been set by Xaml");
    }
}

一个问题是您的

OnButtonClicked
方法创建了 SunViewModel
new
实例,该实例根本没有真正绑定到任何东西。

async void OnButtonClicked(object sender, EventArgs args)
{
    .
    .
    .
    // This is probably not what you're intending to do.
    SunViewModel sunViewModel = new SunViewModel();
    sunViewModel.zenith = 90;
    sunViewModel.Todaydate = selectDate;
    sunViewModel.Longdeg = townLong;
    sunViewModel.Latdeg = townLat;
    sunViewModel.SunriseModel();
}

解决方案

当点击按钮时,它需要作用于已经绑定到视图的

SunViewModel
实例。处理
Clicked
事件的方式没问题,但将命令放入 ViewModel 中会更直接一些,以便它可以随时访问所需的属性。

class SunViewModel : INotifyPropertyChanged
{
    public SunViewModel()
    {
        ButtonClickedCommand = new Command(OnButtonClicked);
    }
    public ICommand ButtonClickedCommand { get; private set; }
    private void OnButtonClicked(object o)
    {
        // Simulate this method by randomizing times that are
        // up to an hour before 6 AM and up to an hour after 6 PM.
        int sunriseOffsetMinutes = _rando.Next(0, 61);
        MySunriseBinding = new DateTime(dateOnly.Year, dateOnly.Month, dateOnly.Day, 6, 0, 0)
                            .AddMinutes(-sunriseOffsetMinutes);
        int sunsetOffsetMinutes = _rando.Next(0, 61); 
        MySunsetBinding = new DateTime(dateOnly.Year, dateOnly.Month, dateOnly.Day, 18, 0, 0)
                            .AddMinutes(sunsetOffsetMinutes);

    }
    Random _rando = new Random();
    private static DateOnly dateOnly => new DateOnly(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day);
    public DateTime MySunriseBinding
    {
        get => _mySunriseBinding;
        set
        {
            if (!Equals(_mySunriseBinding, value))
            {
                _mySunriseBinding = value;
                OnPropertyChanged();
            }
        }
    }
    DateTime _mySunriseBinding = new DateTime(dateOnly, new TimeOnly(6, 0));

    public DateTime MySunsetBinding
    {
        get => _mySunsetBinding;
        set
        {
            if (!Equals(_mySunsetBinding, value))
            {
                _mySunsetBinding = value;
                OnPropertyChanged();
            }
        }
    }
    DateTime _mySunsetBinding = new DateTime(dateOnly, new TimeOnly(18, 0));


    public event PropertyChangedEventHandler? PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName]string? propertyName = null)=>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

xaml 中唯一需要不同的是绑定命令而不是

Clicked
事件:

<Button 
    Scale="0.9"
    BorderWidth="5"
    Text="Submit" 
    FontSize="Title" 
    FontAttributes="Bold" 
    x:Name="TownSubmit"  
    Command="{Binding ButtonClickedCommand}" />
© www.soinside.com 2019 - 2024. All rights reserved.