Xamarin forms: string binding isn't updating in. the view when property is set inside a timer method. why no work?

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

Xamarin forms: string binding isn't updating in. the view when property is set inside a timer method. why no work?

问题

Here is the translation of the code parts you provided:

View Model:

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));
    }
}

View:

<?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>

Code Behind:

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        MainViewModel ViewModel = new MainViewModel();
    }
}
英文:

Trying to constantly update a string in a view. using MVVM so have set up my view model and set up a timer to constantly update the string with the current time.

timer is in the constructor of the view model and the constructor is called in the constructor of the code-behind for the view.

for some reason any time i set the string property within the method in the timer it doesn't update the view? I have a console write line within the timer method that works as expected. Why isn't the view getting the updated string ?

View Model

public class MainViewModel
	{

        //
        TimeIntervals Interval = TimeIntervals.Seconds;

        int DurationOfInterval = 1;

        

        public MainViewModel()
		{
            
            TimeLeft = &quot;2023.5.18&quot;;

            DateTime LastRecordedTime = DateTime.MinValue;

            Device.StartTimer(TimeSpan.FromSeconds(3), () =&gt;
            { 

                
                DateTime CurrentTime = DateTime.Now;

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

                       
                TimeLeft = CurrentTime.ToString();

                return true;



            });

        }

        string timeLeft;

        public string TimeLeft
        {
            set { SetProperty(ref timeLeft, value); }
            get { return timeLeft; }
        }
       
        bool SetProperty&lt;T&gt;(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));
        }



View

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

    &lt;ContentPage.BindingContext&gt;
        &lt;local:MainViewModel/&gt;

    &lt;/ContentPage.BindingContext&gt;

    &lt;StackLayout Background=&quot;black&quot;&gt;
        &lt;Grid ColumnDefinitions=&quot;3*,1*&quot; BackgroundColor=&quot;Black&quot; &gt;
            &lt;Grid Grid.Column=&quot;0&quot; RowDefinitions = &quot;2*,400,1*&quot;  Padding=&quot;5&quot; &gt;
                &lt;Grid Grid.Column=&quot;0&quot; ColumnDefinitions=&quot;50,200&quot; &gt;
                    &lt;Button Grid.Column=&quot;0&quot; Text=&quot;Config&quot; Command=&quot;{Binding goToSettings}&quot;/&gt;

                &lt;/Grid&gt;
            
                &lt;Grid  Grid.Row=&quot;1&quot; Grid.Column=&quot;0&quot; RowDefinitions=&quot;2*,2*&quot; ColumnDefinitions=&quot;2*,2*,2*&quot;  Padding=&quot;5&quot;&gt;
                    &lt;Label Grid.Column=&quot;0&quot; Grid.Row=&quot;0&quot; VerticalOptions=&quot;CenterAndExpand&quot; FontSize=&quot;Title&quot; Text=&quot;{Binding TimeSpent}&quot; BackgroundColor=&quot;Purple&quot;/&gt;
                    &lt;Label Grid.Column=&quot;0&quot; Grid.Row=&quot;1&quot; VerticalOptions=&quot;CenterAndExpand&quot; FontSize=&quot;Title&quot; Text=&quot;{Binding TimeLeft}&quot; BackgroundColor=&quot;Purple&quot;/&gt;

                
                &lt;/Grid&gt;
                
            



            &lt;/Grid&gt;
            &lt;StackLayout Grid.Column=&quot;1&quot; Rotation=&quot;-90&quot; VerticalOptions=&quot;CenterAndExpand&quot; HorizontalOptions=&quot;FillAndExpand&quot; ScaleX=&quot;5&quot; Background=&quot;black&quot; &gt;
                    
                    &lt;ProgressBar ProgressColor=&quot;Bisque&quot; Progress=&quot;{Binding LifeProgress}&quot; VerticalOptions=&quot;FillAndExpand&quot; HorizontalOptions=&quot;FillAndExpand&quot; ScaleY=&quot;20&quot; &gt;&lt;/ProgressBar&gt;

            &lt;/StackLayout&gt;
        &lt;/Grid&gt;
    &lt;/StackLayout&gt;

&lt;/ContentPage&gt;

Code Behind

public partial class MainPage : ContentPage
    {
       

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



        }

        
      
    }

答案1

得分: 0

以下是翻译好的内容:

还有一个有趣的地方,视图在没有实现 INotifyPropertyChanged 的情况下在计时器外部更新了。

对于这一点,您还需要为您的视图模型实现 INotifyPropertyChanged 接口。

从官方文档 ViewModels and 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);

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

您可以在此处参考整个代码:

public class MainViewModel : INotifyPropertyChanged
{
    // ... (其余代码未列出)

    public MainViewModel()
    {
        // ... (其余代码未列出)

        UpdateCommand = new Command(updateTimeMethod);
    }

    // ... (其余代码未列出)
}

希望这些翻译对您有所帮助。

英文:

> still interesting how the view updated outside the timer without
> implementing INotifyPropertyChanged.

For this, you also need to implement interface INotifyPropertyChanged for your view model.

From official document ViewModels and Property-Change Notifications,we could find that:

> The ViewModel is the data-binding source. The ViewModel does not
> define bindable properties, but it does implement a notification
> mechanism that allows the binding infrastructure to be notified when
> the value of a property changes. This notification mechanism is the
> INotifyPropertyChanged interface, which defines a single event named
> PropertyChanged. A class that implements this interface generally
> fires the event when one of its public properties changes value.

So, you need to implement interface INotifyPropertyChanged and call function OnPropertyChanged for the properties that you want to update the UI automatically while changing the value of these properties.

For example:

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

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

Besides,you can change the value of the property at any time according to your needs, and as long as you implement interface INotifyPropertyChanged for your view model and call OnPropertyChanged for your property, the UI will be automatically updated.

Based on your code,I added a command to the viewmodel and bind to the button to change the value of property TimeLeft and TimeSpent,then the UI will update itself.

  public ICommand UpdateCommand { get; set; }

  UpdateCommand = new Command(updateTimeMethod);

method updateTimeMethod

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

    TimeSpent = &quot;TimeSpent 2&quot;;
}

You can refer to the whole code here:

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

    int DurationOfInterval = 1;

    public ICommand UpdateCommand { get; set; }

    public MainViewModel()
    {

        TimeLeft = &quot;2023.5.18&quot;;

        TimeSpent = &quot;TimeSpent 1&quot;;

        DateTime LastRecordedTime = DateTime.MinValue;

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

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


            TimeLeft = CurrentTime.ToString();

            return true;
        });

        UpdateCommand = new Command(updateTimeMethod);
    }

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

        TimeSpent = &quot;TimeSpent 2&quot;;
    }

    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&lt;T&gt;(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));
    }
}

MainPage.xaml

&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt; 
&lt;ContentPage xmlns=&quot;http://xamarin.com/schemas/2014/forms&quot;
             xmlns:x=&quot;http://schemas.microsoft.com/winfx/2009/xaml&quot; 
             xmlns:mvvmapp521=&quot;clr-namespace:MvvMApp521&quot;
             x:Class=&quot;MvvMApp521.MainPage&quot;&gt;

    &lt;ContentPage.BindingContext&gt;
        &lt;mvvmapp521:MainViewModel/&gt;
    &lt;/ContentPage.BindingContext&gt;

    &lt;StackLayout Background=&quot;black&quot;&gt;
        &lt;Label  VerticalOptions=&quot;CenterAndExpand&quot; FontSize=&quot;Title&quot; Text=&quot;{Binding TimeSpent}&quot; BackgroundColor=&quot;Purple&quot;/&gt;
        &lt;Label  VerticalOptions=&quot;CenterAndExpand&quot; FontSize=&quot;Title&quot; Text=&quot;{Binding TimeLeft}&quot; BackgroundColor=&quot;Purple&quot;/&gt;

        &lt;Button   Text=&quot;reset value&quot; HorizontalOptions=&quot;FillAndExpand&quot; HeightRequest=&quot;50&quot;    Command=&quot;{Binding UpdateCommand}&quot;/&gt;
    &lt;/StackLayout&gt;

&lt;/ContentPage&gt;

Note:

Since you have set the MainViewModel for the page on Yourpage.xaml, you don't need to set it again on the Yourpage.xaml.cs, so please remove the code :

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

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

huangapple
  • 本文由 发表于 2023年5月21日 04:58:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76297330.html
匿名

发表评论

匿名网友

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

确定