我有一个带有名称选择器、数据选择器、按钮和一些标签的用户界面,按下按钮时,我使用选定的名称从数据库中获取一堆数据。然后我想使用视图模型来计算数字并通过将变量绑定到我的 UI 返回日出和日落时间。
使用断点,我可以看到一切正常,标签绑定会立即获取文本,直到我继续调试。我试过:
在过程括号内传递变量
在虚拟机中设置绑定变量并设置 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;
}
}
为了重现您描述的行为,我将您的代码作为 .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}" />