Xamarin形式:字符串绑定未在中更新,在timer方法中设置了视图的when属性,为什么没有工作?

fv2wmkja  于 2023-05-27  发布在  其他
关注(0)|答案(1)|浏览(131)

尝试不断更新视图中的字符串。使用MVVM,所以我已经建立了我的视图模型,并设置了一个计时器,以不断更新字符串与当前时间。
计时器位于视图模型的构造函数中,而构造函数在视图的代码隐藏的构造函数中调用。
由于某种原因,每当我在计时器中的方法中设置字符串属性时,它不会更新视图?我在timer方法中有一个控制台写行,它可以按预期工作。为什么视图没有得到更新后的字符串?
视图模型

public class MainViewModel
    {

        //
        TimeIntervals Interval = TimeIntervals.Seconds;

        int DurationOfInterval = 1;

        

        public MainViewModel()
        {
            
            TimeLeft = "2023.5.18";

            DateTime LastRecordedTime = DateTime.MinValue;

            Device.StartTimer(TimeSpan.FromSeconds(3), () =>
            { 

                
                DateTime CurrentTime = DateTime.Now;

                Console.WriteLine($"time has passed {LastRecordedTime.ToString("dd/MM/yyyy |HH:mm:ss")}");

                       
                TimeLeft = CurrentTime.ToString();

                return true;


            });

        }

        string timeLeft;

        public string TimeLeft
        {
            set { SetProperty(ref timeLeft, value); }
            get { return timeLeft; }
        }
       
        bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string PropertyName = null)
        {
            if (Object.Equals(storage, value))
                return false;
            storage = value;
            OnPropertyChanged(PropertyName);
            return true;
        }

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

查看

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DeathClock.Main"
             x:Class="DeathClock.MainPage">

    <ContentPage.BindingContext>
        <local:MainViewModel/>

    </ContentPage.BindingContext>

    <StackLayout Background="black">
        <Grid ColumnDefinitions="3*,1*" BackgroundColor="Black" >
            <Grid Grid.Column="0" RowDefinitions = "2*,400,1*"  Padding="5" >
                <Grid Grid.Column="0" ColumnDefinitions="50,200" >
                    <Button Grid.Column="0" Text="Config" Command="{Binding goToSettings}"/>

                </Grid>
            
                <Grid  Grid.Row="1" Grid.Column="0" RowDefinitions="2*,2*" ColumnDefinitions="2*,2*,2*"  Padding="5">
                    <Label Grid.Column="0" Grid.Row="0" VerticalOptions="CenterAndExpand" FontSize="Title" Text="{Binding TimeSpent}" BackgroundColor="Purple"/>
                    <Label Grid.Column="0" Grid.Row="1" VerticalOptions="CenterAndExpand" FontSize="Title" Text="{Binding TimeLeft}" BackgroundColor="Purple"/>

                
                </Grid>
                
            


            </Grid>
            <StackLayout Grid.Column="1" Rotation="-90" VerticalOptions="CenterAndExpand" HorizontalOptions="FillAndExpand" ScaleX="5" Background="black" >
                    
                    <ProgressBar ProgressColor="Bisque" Progress="{Binding LifeProgress}" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" ScaleY="20" ></ProgressBar>

            </StackLayout>
        </Grid>
    </StackLayout>

</ContentPage>

代码隐藏

public partial class MainPage : ContentPage
    {
       

       
        public MainPage()
        {
            InitializeComponent();
            MainViewModel ViewModel = new MainViewModel();


        }

        
      
    }
yqlxgs2m

yqlxgs2m1#

仍然有趣的是,视图如何在没有实现INotifyPropertyChanged的情况下在计时器之外更新。
为此,您还需要为视图模型实现接口INotifyPropertyChanged
从官方文档ViewModels和Property-Change Notifications中,我们可以发现:
ViewModel是数据绑定源。ViewModel没有定义可绑定的属性,但是它实现了一个通知机制,允许在属性值更改时通知绑定基础结构。此通知机制是INotifyPropertyChanged接口,它定义了一个名为PropertyChanged的事件。实现此接口的类通常在其某个公共属性更改值时激发事件。
因此,您需要实现接口INotifyPropertyChanged,并为您希望在更改这些属性的值时自动更新UI的属性调用函数OnPropertyChanged
例如:

public string TimeLeft
    {
        set { SetProperty(ref timeLeft, value); }
        get { return timeLeft; }
    }

    string timeSpent;
    public string TimeSpent
    {
        set { SetProperty(ref timeSpent, value); }
        get { return timeSpent; }
    }

此外,您可以根据需要随时更改属性的值,只要为您的视图模型实现接口INotifyPropertyChanged,并为您的属性调用OnPropertyChanged,UI就会自动更新。
根据您的代码,我向视图模型添加了一个命令,并绑定到按钮以更改属性TimeLeftTimeSpent的值,然后UI将自行更新。

public ICommand UpdateCommand { get; set; }

  UpdateCommand = new Command(updateTimeMethod);

方法updateTimeMethod

private void updateTimeMethod(object obj)
{
    TimeLeft = "2023.5.21";

    TimeSpent = "TimeSpent 2";
}

你可以在这里看到完整的代码:

public class MainViewModel: INotifyPropertyChanged 
{
   // TimeIntervals Interval = TimeIntervals.Seconds;

    int DurationOfInterval = 1;

    public ICommand UpdateCommand { get; set; }

    public MainViewModel()
    {

        TimeLeft = "2023.5.18";

        TimeSpent = "TimeSpent 1";

        DateTime LastRecordedTime = DateTime.MinValue;

        Device.StartTimer(TimeSpan.FromSeconds(3), () =>
        {
            DateTime CurrentTime = DateTime.Now;

            Console.WriteLine($"time has passed {LastRecordedTime.ToString("dd/MM/yyyy |HH:mm:ss")}");

            TimeLeft = CurrentTime.ToString();

            return true;
        });

        UpdateCommand = new Command(updateTimeMethod);
    }

    private void updateTimeMethod(object obj)
    {
        TimeLeft = "2023.5.21";

        TimeSpent = "TimeSpent 2";
    }

    string timeLeft;

    public string TimeLeft
    {
        set { SetProperty(ref timeLeft, value); }
        get { return timeLeft; }
    }

    string timeSpent;
    public string TimeSpent
    {
        set { SetProperty(ref timeSpent, value); }
        get { return timeSpent; }
    }

    bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string PropertyName = null)
    {
        if (Object.Equals(storage, value))
            return false;
        storage = value;
        OnPropertyChanged(PropertyName);
        return true;
    }

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

主页.xaml

<?xml version="1.0" encoding="utf-8" ?> 
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:mvvmapp521="clr-namespace:MvvMApp521"
             x:Class="MvvMApp521.MainPage">

    <ContentPage.BindingContext>
        <mvvmapp521:MainViewModel/>
    </ContentPage.BindingContext>

    <StackLayout Background="black">
        <Label  VerticalOptions="CenterAndExpand" FontSize="Title" Text="{Binding TimeSpent}" BackgroundColor="Purple"/>
        <Label  VerticalOptions="CenterAndExpand" FontSize="Title" Text="{Binding TimeLeft}" BackgroundColor="Purple"/>

        <Button   Text="reset value" HorizontalOptions="FillAndExpand" HeightRequest="50"    Command="{Binding UpdateCommand}"/>
    </StackLayout>

</ContentPage>

注意事项:
由于您已经在Yourpage.xaml上为页面设置了MainViewModel,因此不需要在Yourpage.xaml.cs上再次设置,因此请删除代码:

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

        // comment out the following code here
        // MainViewModel ViewModel = new MainViewModel();
    }   
}

相关问题