【问题标题】:Property changed is not being triggered inside a Task属性更改未在任务内触发
【发布时间】:2020-10-22 10:13:51
【问题描述】:

我有一个简单的应用程序,当某些任务正在运行时,我会显示进度环,并在完成后立即隐藏进度环。这段代码的问题是进度条永远不会折叠。我在值转换器类中保留了一个断点,即使在值更改后它也永远不会收到 false 值。因此,ProgressRing 永远不会崩溃。请帮忙。

这是我的视图模型

public class TestVM : INotifyPropertyChanged
    {
        private bool _isRingVisible;
        public bool IsRingVisible
        {
            get => _isRingVisible;
            set
            {
                _isRingVisible = value;
                OnPropertyChanged(nameof(IsRingVisible));
            }
        }
        public TestVM()
        {
            Task.Run(async () => await DoSomething());
        }

        private async Task DoSomething()
        {
            IsRingVisible = true;
            await Task.Delay(5000);
            IsRingVisible = false; //Value set to false but when I have a break point in the value converter class, it never receives this value.
        }

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

在 xaml 中,我有一个简单的 UI,如下所示,

<Page.Resources>
        <converter:BoolToVisibilityConverter x:Key="boolToVisibility"/>
    </Page.Resources>
<Grid>
        <Border x:Name="BdrProgressRing" 
                    Grid.Row="0" 
                    Grid.RowSpan="2" 
                    Background="Red"
                    VerticalAlignment="Center"
                    Opacity="0.6" 
                    Visibility="{x:Bind vm.IsRingVisible,Mode=OneWay,Converter={StaticResource boolToVisibility}}">
        </Border>
        <ProgressRing x:Name="PgRing" 
                          Grid.Row="0" 
                          Grid.RowSpan="2"
                          Visibility="{Binding ElementName=BdrProgressRing, Path=Visibility}"
                          IsActive="True"  
                          VerticalAlignment="Center"
                          Width="90"
                          Height="90"/>
    </Grid>

这是我的 xaml.cs

public sealed partial class MainPage : Page
    {
        public TestVM vm { get; set; }
        public MainPage()
        {
            this.InitializeComponent();
            vm = new TestVM();
            this.DataContext = this;
        }
    }

【问题讨论】:

  • 谢谢它的工作。由于我在 Task 中调用它,我猜我需要使用 Dispatcher。
  • 我一定会读到的,谢谢。
  • 尽可能不要使用Dispatcher。它可以使代码看起来像怪物。如果你想在执行过程中从并发Task 向 UI 线程发送一些东西,你可以使用同步回调类Progress,它实现了IProgress 接口。就像 while-true 一样简单。它在构造它的上下文中执行它的委托。

标签: c# uwp async-await


【解决方案1】:

改变

Task.Run(async () => await DoSomething()) ;

_ = DoSomething();

您可能只允许从主 UI 线程更改属性,而不是从池 Task。详细了解 WPF 中的同步上下文。


但是,以上是不好的做法。应该等待任何异步方法。简单地将 DoSomething() 返回的任务分配给局部变量是不够的。

由于您不能在构造函数中等待,因此视图模型应该有一个公共的可等待方法,该方法实际上由调用者等待,例如

public TestVM()
{
}

public Task Initialize()
{
    return DoSomething();
}

然后在视图中的异步Loaded事件处理程序中调用await vm.Initialize();

public MainPage()
{
    InitializeComponent();
    vm = new TestVM();
    DataContext = this;

    Loaded += async (s, e) => await vm.Initialize();
}

【讨论】:

  • 嗨@aepot你能帮我写这篇文章吗stackoverflow.com/questions/62687039/…我认为它和这个一样,但它并没有解决我的问题。
  • @Clemens 感谢您的有用编辑。我更喜欢保留它。但是我看不到像 Fire-and-Forget 这样的直接调用和 async void lambda 在更新的答案中用于 Load 之间的技术差异。逻辑上的区别在于您必须在哪里处理可能的异常:DoSomethingAsync 或 lambda。因此,我不能说这是不安全的“即发即弃”是不好的做法。在这两种方式中,开发人员都必须注意可能的Exception 捕获。我也喜欢this 方法。
  • 这与任何例外无关。关键是总是等待一个可等待的方法 - 除了事件处理程序,这是不可能的。
  • 从不声明async void方法,除非它是一个事件处理程序。
  • "当它完全符合我的需要时,我不能使用某些实现?。这不是重点。请记住,您正在阅读 StackOverflow 上的答案许多非专家的人。
猜你喜欢
  • 1970-01-01
  • 2018-07-21
  • 1970-01-01
  • 2014-06-14
  • 1970-01-01
  • 2017-09-14
  • 2011-02-08
  • 2021-09-18
  • 1970-01-01
相关资源
最近更新 更多