如何使用MVVM模式在常规中更新UI属性

3
我想建立一个倒计时器。问题是我的解决方案只显示10秒开始值和1秒结束值。当然,我已经实现了INotifyPropertyChanged接口。对于这个解决方案有什么建议吗?
<Button Content="Generuj"  Command="{Binding ButtonStart}"></Button>
<TextBox  Text="{Binding Counter, Mode=OneWay}"></TextBox>

private void ButtonStartClick(object obj)
{
    for (int i = 10; i > 0; i--)
    {
         System.Threading.Thread.Sleep(1000);
         Counter = i;
    }
}

计数器必须是一个属性,必须实现INotifyPropertyChanged接口。Thread.Sleep(1000); 是从GUI线程运行的 -> 这会阻塞对GUI的更新。您将注意到10秒的休眠,然后计数器被设置为0。 - fstam
4
您已经从按钮单击开始运行代码。此代码正在主线程上运行,这会锁定UI,直到代码运行完成。因此,在代码完成运行之前,UI无法更新。这意味着UI在您的代码运行完成之前不会显示属性更新。要在代码运行时更新UI,您需要在BackgroundWorker或异步任务中运行代码。 - Stewbob
3个回答

3

使用Thread.Sleep会冻结您的GUI。尝试使用计时器来实现您的目的。 计时器将与GUI线程同时运行,因此不会冻结它。 另外,您需要实现PropertyChanged事件来更新计数器。 还要确保设置您的DataContext。

    //create a dependency property you can bind to (put into class)
    public int Counter
    {
        get { return (int)this.GetValue(CounterProperty); }
        set { this.SetValue(CounterProperty, value); }
    }

    public static readonly DependencyProperty CounterProperty =
        DependencyProperty.Register(nameof(Counter), typeof(int), typeof(MainWindow), new PropertyMetadata(default(int)));


    //Create a timer that runs one second and decreases CountDown when elapsed (Put into click event)
    Timer t = new Timer();
    t.Interval = 1000;
    t.Elapsed += CountDown;
    t.Start();

    //restart countdown when value greater one (put into class)
    private void CountDown(object sender, ElapsedEventArgs e)
    {
        if (counter > 1)
        {
            (sender as Timer).Start();
        }
        Counter--;
    }

0

您可以使用async await来引入轻量级延迟。

与计时器相比,它的主要优点是没有留下委托挂钩和运行的风险。

在此ViewModel中,我使用mvvmlight,但任何ICommand的实现都可以。

 …..
using System.Threading.Tasks;
using GalaSoft.MvvmLight.CommandWpf;
namespace wpf_99
{
public class MainWindowViewModel : BaseViewModel
{
    private int counter =10;

    public int Counter
    {
        get { return counter; }
        set { counter = value; RaisePropertyChanged(); }
    }

    private RelayCommand countDownCommand;
    public RelayCommand CountDownCommand
    {
        get
        {
            return countDownCommand
            ?? (countDownCommand = new RelayCommand(
             async () =>
             {
                 for (int i = 10; i > 0; i--)
                 {
                     await Task.Delay(1000);
                     Counter = i;
                 }
             }
             ));
        }
    }

视图并不多,它当然绑定到计数器:

<Grid>
    <StackPanel>
    <TextBlock Text="{Binding Counter}"/>
        <Button Content="Count" Command="{Binding CountDownCommand}"/>
    </StackPanel>
</Grid>

0
你还可以在单独的线程中运行计数器。
Task.Run(() =>
             {
                 for (int i = 10; i > 0; i--)
                 {
                     Counter = i;
                     Thread.Sleep(1000);
                 }
             });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接