英文:
.net Maui Community Toolkit - Observable Object - not able to update view
问题
I am learning .net Maui and have not had a lot of experience with Xamarin or .net Maui. Could use some hints on Observable Objects and Properties
我正在学习 .net Maui,对 Xamarin 或 .net Maui 经验有限。需要一些关于可观察对象和属性的提示。
I have an AddLogEntry page with an associated AddLogEntryViewModel (which inherits from BaseViewModel which in turn inherits from ObservableObject, which is used to add a new log entry.
我有一个 AddLogEntry 页面,它关联了一个 AddLogEntryViewModel(该 ViewModel 继承自 BaseViewModel,而 BaseViewModel 又继承自 ObservableObject,用于添加新的日志条目。
The view model has the following RelayCommands:
这个视图模型有以下的 RelayCommands(命令):
-
AddLogEntry - uses LogEntryService to abstract from the database stuff
-
GetCurrentPosition - uses Maui IGeolocation to get a current position from the device and intended to update the location in the view.
-
AddLogEntry - 使用 LogEntryService 来抽象数据库相关的操作。
-
GetCurrentPosition - 使用 Maui 的 IGeolocation 来获取设备的当前位置,并打算更新视图中的位置信息。
When the view is opened it gives me a new instance of LogEntry and correctly gets the current date and time and displays AddLogEntryPage
当视图被打开时,它会给我一个新的 LogEntry 实例,正确获取当前日期和时间,并显示 AddLogEntryPage。
However, when I call GetCurrentPosition from the page using Command="{Binding GetCurrentPositionCommand}" attached to a button I have done something wrong.
然而,当我从页面上调用 Command="{Binding GetCurrentPositionCommand}" 附加到一个按钮上时,我做错了一些事情。
I can see by adding appropriate breakpoints that the current location is being returned and appropriate properties of the LogEntry object are updated. However, the view is not updated with this information even though I think it is bound to the appropriate properties of LogEntry.
通过添加适当的断点,我可以看到当前位置被返回,并且 LogEntry 对象的适当属性被更新。然而,尽管我认为它与 LogEntry 的适当属性绑定,但视图没有更新此信息。
I have added the View, ViewModel, BaseViewModel, and LogEntry class below.
我在下面添加了视图、ViewModel、BaseViewModel 和 LogEntry 类。
If I save the LogEntry the correct geolocation information is added to the database but not ever displayed dynamically in the view.
如果我保存 LogEntry,正确的地理位置信息会添加到数据库,但在视图中不会动态显示。
I suspect I have misunderstood something - any hints about what I am doing wrong? I hope I have been clear enough - happy to provide anything additional.
我怀疑我对某些事情有误解 - 有关我做错了什么的任何提示吗?我希望我表达得足够清楚 - 乐意提供任何额外的信息。
英文:
I am learning .net Maui and have not had a lot of experience with Xamarin or .net Maui. Could use some hints on Observable Objects and Properties
I have an AddLogEntry page with an associated AddLogEntryViewModel (which inherits from BaseViewModel which in turn inherits from ObservableObject, which is used to add a new log entry.
The view model has the following RelayCommands:
- AddLogEntry - uses LogEntryService to abstract from the database stuff
- GetCurrentPosition - uses Maui IGeolocation to get a current position from the device and intended to update the location in the view.
When the view is opened it gives me a new instance of LogEntry and correctly gets the current date and time and displays AddLogEntryPage
However, when I call GetCurrentPosition from the page using Command="{Binding GetCurrentPositionCommand}" attached to a button I have done something wrong.
I can see by adding appropriate breakpoints that the current location is being returned and appropriate properties of the LogEntry object are updated. However, the view is not updated with this information even though I think it is bound to the appropriate properties of LogEntry.
I have added the View, ViewModel, BaseViewModel and LogEntry class below.
If I save the LogEntry the correct geolocation information is added to the database but not ever displayed dynamically in the view.
I suspect I have misunderstood something - any hints about what I am doing wrong? I hope I have been clear enough - happy to provide anything additional.
AddLogEntryPage (XAML)
<?xml version="1.0" encoding="UTF-8" ?>
<ContentPage
x:Class="Logbook.Views.AddLogEntryPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://schemas.microsoft.com/dotnet/2021/maui/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:gl="clr-namespace:System.Globalization;assembly=mscorlib"
xmlns:converters="clr-namespace:Logbook.Converters"
mc:Ignorable="d"
xmlns:viewmodel="clr-namespace:Logbook.ViewModels"
x:DataType="viewmodel:AddLogEntryViewModel">
<ContentPage.Resources>
<converters:DayOfMonthConverter x:Key="dayOfMonthConverter"/>
</ContentPage.Resources>
<Grid>
<VerticalStackLayout>
<!-- Date and Time -->
<StackLayout>
<Label Text="Date and Time" Style="{StaticResource MediumLabel}"/>
<HorizontalStackLayout>
<DatePicker VerticalOptions="Center"
Format="dd/MM/yyyy"
Style="{StaticResource MediumDatePicker}"
Date="{Binding LogEntry.EntryDateTime.Date}"/>
<Label Style="{StaticResource MediumLabel}"
Text="at"
VerticalOptions="Center"/>
<TimePicker VerticalOptions="Center"
Format="HH:mm"
Style="{StaticResource MediumTimePicker}"
Time="{Binding LogEntry.EntryDateTime.TimeOfDay}"/>
</HorizontalStackLayout>
<!--Line under Log entry date and time-->
<BoxView
Style="{StaticResource Seperator}"/>
</StackLayout>
<!-- Location -->
<StackLayout>
<Label Text="Location" Style="{StaticResource MediumLabel}"/>
<StackLayout Orientation="Horizontal">
<StackLayout>
<Label Text="Latitude" FontSize="16"/>
<Entry Text="{Binding LogEntry.Latitude}" Placeholder="Enter the Latitude in decimal degrees"/>
</StackLayout>
<StackLayout>
<Label Text="Longitude" FontSize="16"/>
<Entry Text="{Binding LogEntry.Longitude}" Placeholder="Enter the Longitude in decimal degrees"/>
</StackLayout>
</StackLayout>
<Button Text="Get Current Position" Command="{Binding GetCurrentPositionCommand}" Style="{StaticResource PrimaryButton}"/>
</StackLayout>
<!-- Crew -->
<StackLayout>
<StackLayout>
<Label Text="Crew" Style="{StaticResource MediumLabel}"/>
<Entry Text="{Binding LogEntry.Crew}" Placeholder="Enter crew members"/>
</StackLayout>
<Button Text="Get Test Crew" Command="{Binding TestCrewCommand}" Style="{StaticResource PrimaryButton}"/>
</StackLayout>
<!-- Weather Conditions -->
<StackLayout>
<Label Text="Weather Conditions" Style="{StaticResource MediumLabel}"/>
<Picker Title="Select Weather Conditions" SelectedItem="{Binding LogEntry.WeatherConditions}">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Sunny</x:String>
<x:String>Cloudy</x:String>
<x:String>Rainy</x:String>
<x:String>Windy</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>
</StackLayout>
<!-- Notes -->
<StackLayout>
<Label Text="Notes" Style="{StaticResource MediumLabel}"/>
<Editor Text="{Binding LogEntry.Notes}" Placeholder="Enter additional notes" HeightRequest="100"/>
</StackLayout>
<!-- Distance Sailed -->
<StackLayout>
<Label Text="Distance Sailed" Style="{StaticResource MediumLabel}"/>
<Entry Text="{Binding LogEntry.DistanceSailed}" Placeholder="Enter the distance sailed"/>
</StackLayout>
<!-- Save Button -->
<Button Command="{Binding AddLogEntryCommand}" Text="Save Log Entry" Style="{StaticResource PrimaryButton}"/>
</VerticalStackLayout>
</Grid>
</ContentPage>
ViewModel
namespace Logbook.ViewModels;
public partial class AddLogEntryViewModel : BaseViewModel
{
[ObservableProperty]
LogEntry logEntry = new();
private readonly ILogEntryService logEntryService;
public AddLogEntryViewModel(ILogEntryService logEntryService)
{
this.logEntryService = logEntryService;
logEntry.EntryDateTime = DateTime.Now;
}
[RelayCommand]
async void AddLogEntry()
{
var response = await logEntryService.AddLogEntry(logEntry);
if (response > 0)
{
await Shell.Current.DisplayAlert("Record Added", "Employee Details Successfully submitted", "OK");
}
else
{
await Shell.Current.DisplayAlert("Not Addedd", "Something went wrong with the Employees Details", "OK");
}
}
[RelayCommand]
async Task GetCurrentPosition()
{
try
{
var request = new GeolocationRequest(GeolocationAccuracy.Medium);
var location = await Geolocation.GetLocationAsync(request);
if (location != null)
{
logEntry.Latitude = location.Latitude;
logEntry.Longitude = location.Longitude;
}
else
{
// Handle case where location is null
// Display an error message or take appropriate action
}
}
catch (FeatureNotSupportedException)
{
// Handle not supported on device exception
// Display an error message or take appropriate action
}
catch (PermissionException)
{
// Handle permission exception
// Display an error message or take appropriate action
}
catch (Exception)
{
// Handle other exceptions
// Display an error message or take appropriate action
}
}
}
BaseViewModel
public partial class BaseViewModel : ObservableObject
{
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsNotBusy))]
bool isBusy;
[ObservableProperty]
string title;
public BaseViewModel()
{
}
public bool IsNotBusy => !IsBusy;
}
Model
namespace Logbook.Models
{
[Table("LogEntries")]
public partial class LogEntry
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public DateTime EntryDateTime { get; set; }
public string Crew { get; set; }
public string Location { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public string WeatherConditions { get; set; }
public string Notes { get; set; }
public double DistanceSailed { get; set; }
}
}
答案1
得分: 2
但是,此视图不会根据此信息进行更新。
这是因为您的模型 LogEntry 类没有扩展 ObservableObject 类。所以当您在视图模型中更改 LogEntry 属性时,它不会通知 UI。
因此,您可以尝试两种解决方案:
- 使 LogEntry 类扩展 ObservableObject 并在 LogEntry 中的每个属性上添加 [ObservableProperty]。但首先需要将每个值的第一个字母更改为小写。
- 如果您只想让
GetCurrentPosition()
命令能够更新 UI。您可以创建一个 LogEntry 类的新实例,例如LogEntry temp = new ()
,并将所有属性和新位置复制到 temp 中。然后您可以使用logEntry = temp
。因为 logEntry 是可观察属性,它将通知 UI 进行更新。
英文:
> However, the view is not updated with this information
This is because your model LogEntry class didn't extend the ObservableObject class. So when you change the peroperty of the LogEntry proerpty in the viewmodel. It will not notify the UI.
So you can try the two solutions:
- Make the LogEntry class extend the ObservableObject and add [ObservableProperty] on the each property in the LogEntry. But you need to change each value's first letter to lowercase at first.
- If you just want the
GetCurrentPosition()
command can update the UI. You can create a new instance of the LogEntry class such asLogEntry temp = new ()
and copy all the properties and the new location to the temp. And then you can use thelogEntry = temp
. Because the logEntry is observableproperty, it will notify the ui to update.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论