为什么在PowerMode.Resume之后视图(可见性)不改变?

时间:2019-04-04 11:53:43

标签: c# wpf view timer task

我编写了一个程序,可以在一段时间后,特定时间或每天特定时间关闭(关闭,重新启动,休眠,挂起)PC。

当我第一次启动该应用程序时,一切(包括视图)都可以正常工作。但是当PC在挂起/休眠后恢复时,UI似乎不再响应。

我有一个基本的viemodel:

class ViewModel : INotifyPropertyChanged
{        

    public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };

    private bool _runCountdown;
    public bool RunCountdown
    {
        get { return _runCountdown; }
        set
        {
            _runCountdown = value;
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(RunCountdown)));
            if (RunCountdown)
            {
                TimeRemainingVisibility = Visibility.Visible;
            }
            else
            {
                TimeRemainingVisibility = Visibility.Hidden;
            }
        }
    }

    private Visibility _timeRemainingVisibility;
    public Visibility TimeRemainingVisibility
    {
        get { return _timeRemainingVisibility; }
        set
        {
            _timeRemainingVisibility = value;
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(TimeRemainingVisibility)));
        }
    } 
//...
}
UI绑定到的

 <Grid>
    <StackPanel Orientation="Vertical" Margin="0,0">
        <Grid Margin=" 4,2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="130"></ColumnDefinition>
                <ColumnDefinition Width="160"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
                <Label Grid.Column="0" Margin="16,0,0,0">Shutdown mode:</Label>
                <ComboBox Grid.Column="1" Name="CB_Mode" HorizontalAlignment="Left"  VerticalAlignment="Center" Width="160"  SelectedItem="{Binding ShutdownMode}" ToolTip="{Binding Shutdowndescription}"/>                
        </Grid>
        <Expander Header="Shutdown timer:" IsExpanded="True">
            <Grid Margin=" 4,2">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="130"></ColumnDefinition>
                    <ColumnDefinition Width="160"></ColumnDefinition>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <Rectangle Grid.Column="1" Margin="0,2" StrokeThickness="2" Stroke="Gray" Width="160" HorizontalAlignment="Left"/>
                <StackPanel Grid.Column="1" Orientation="Horizontal" Margin="5">
                    <TextBox Style="{StaticResource TimeTextBox}" Text="{Binding Path=Timer[0]}"/>
                    <Label Style="{StaticResource TimeLabel}">h</Label>
                    <TextBox Style="{StaticResource TimeTextBox}" Text="{Binding Path=Timer[1]}"/>
                    <Label Style="{StaticResource TimeLabel}">min</Label>
                    <TextBox Style="{StaticResource TimeTextBox}" Text="{Binding Path=Timer[2]}"/>
                    <Label Style="{StaticResource TimeLabel}">sec</Label>
                </StackPanel>
                <Button Grid.Column="2" Name="B_StartTimer"  Click="ButtonStart_Click" Style="{StaticResource GreenButton}">Start</Button>
            </Grid>
        </Expander>
        <Expander Header="Shutdown time:" IsExpanded="True">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*"></RowDefinition>
                    <RowDefinition Height="*"></RowDefinition>
                </Grid.RowDefinitions>
                <Grid Grid.Row="0" Margin="4,2">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="130"></ColumnDefinition>
                        <ColumnDefinition Width="160"></ColumnDefinition>
                        <ColumnDefinition Width="*"></ColumnDefinition>
                    </Grid.ColumnDefinitions>
                    <DatePicker Grid.Column="1" Name="DP_ShutdownDate" SelectedDate="{Binding ShutdownDate}" Margin="0" Visibility="{Binding DateVisibility}"/>
                    <CheckBox Grid.Column="2" Name="CB_ShutdownDayly" Content="dayly" HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding Dayly}"/>
                </Grid>
                <Grid Grid.Row="1" Margin="4,2">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="130"></ColumnDefinition>
                        <ColumnDefinition Width="160"></ColumnDefinition>
                        <ColumnDefinition Width="*"></ColumnDefinition>
                    </Grid.ColumnDefinitions>
                    <Rectangle Grid.Column="1" Margin="0,2" StrokeThickness="2" Stroke="Gray" Width="160" HorizontalAlignment="Left"></Rectangle>
                    <StackPanel Grid.Column="1" Orientation="Horizontal" Margin="5">
                        <TextBox Style="{StaticResource TimeTextBox}"  Text="{Binding Path=Date[0]}"/>
                        <Label Style="{StaticResource TimeLabel}">h</Label>
                        <TextBox Style="{StaticResource TimeTextBox}" Text="{Binding Path=Date[1]}"/>
                        <Label Style="{StaticResource TimeLabel}">min</Label>
                        <TextBox Style="{StaticResource TimeTextBox}" Text="{Binding Path=Date[2]}"/>
                        <Label Style="{StaticResource TimeLabel}">sec</Label>
                    </StackPanel>
                    <Button Grid.Column="2" Click="ButtonStart_Click" Style="{StaticResource GreenButton}">Start</Button>
                </Grid>
            </Grid>
        </Expander>
        <Grid Margin=" 4,2" Visibility="{Binding TimeRemainingVisibility}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="130"></ColumnDefinition>
                <ColumnDefinition Width="160"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Label Grid.Column="0" Margin="16,0,0,0">Time Remaining:</Label>
            <StackPanel Orientation="Vertical" Grid.Column="1" ClipToBounds="True" Margin="0,5,0,0">
                <TextBox Text="{Binding Path=TimeToShutdown, StringFormat=HH:mm:ss}" ClipToBounds="True" VerticalContentAlignment="Center" Padding="5,0" BorderThickness="0"/>
                <TextBox Text="{Binding Path=DisplayOnlyShutdownDate, StringFormat=yyyy-MM-dd HH:mm:ss}" ClipToBounds="True"  VerticalContentAlignment="Center" Padding="5,0" BorderThickness="0"/>
            </StackPanel>
            <Button Grid.Column="2" Click="ButtonStop_Click" IsCancel="True" Style="{StaticResource RedButton}">Stop</Button>
        </Grid>
    </StackPanel>
</Grid>

最后一个网格包含相关信息。样式仅包含样式信息,没有行为,命令等。

在后面的代码中,将ViewModels RunCountdown设置为true或false:

    private void OnPowerChange(object s, PowerModeChangedEventArgs e)
    {
        switch (e.Mode)
        {
            case PowerModes.Resume:
                _vm = XmlHelper.ReadConfig(CONFIGPATH);
                if (_vm.Dayly)
                {
                    ButtonStart_Click(new Button(), null);
                    if (!ni.Visible)
                    {
                        ni.Visible = true;
                    }
                }
                break;
            //case PowerModes.Suspend:
            //    ni.Visible = false;
            //    break;
            default: break;
        }
    }

    private void ButtonStart_Click(object sender, RoutedEventArgs e)
    {
        Button startButton = (Button)sender;

        if (!_vm.RunCountdown)
        {
            SetShutdownTime(startButton);
        }
        else
        {
            if (MessageBox.Show("Ein Shutdown Timer läuft bereits.\r\nWollen sie wirklich einen neuen starten?",
                "Timer läuft bereits", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.Yes) == MessageBoxResult.Yes)
            {
                _vm.RunCountdown = false;
                Thread.Sleep(1000);
                SetShutdownTime(startButton);
            }
        }
    }

    private void SetShutdownTime(Button startButton)
    {
        _vm.DisplayOnlyShutdownDate = DateTime.Now;
        if (startButton.Name == "B_StartTimer")
        {
            _vm.DisplayOnlyShutdownDate = _vm.DisplayOnlyShutdownDate.AddHours(Convert.ToDouble(_vm.Timer[0]))
                .AddMinutes(Convert.ToDouble(_vm.Timer[1]))
                .AddSeconds(Convert.ToDouble(_vm.Timer[2]));
        }
        else
        {
            if (!_vm.Dayly)
            {
                _vm.DisplayOnlyShutdownDate = _vm.ShutdownDate;
            }
            else
            {
                _vm.DisplayOnlyShutdownDate = DateTime.Now.Date;
            }
            _vm.DisplayOnlyShutdownDate = _vm.DisplayOnlyShutdownDate.Date.AddHours(Convert.ToDouble(_vm.Date[0]))
                .AddMinutes(Convert.ToDouble(_vm.Date[1]))
                .AddSeconds(Convert.ToDouble(_vm.Date[2]));

            while (_vm.DisplayOnlyShutdownDate < DateTime.Now)
            {
                _vm.DisplayOnlyShutdownDate = _vm.DisplayOnlyShutdownDate.AddDays(1);
            }
        }
        //secondsTimer.Start();
        StartCountdown();

    }
    private void StartCountdown()
    {
        _vm.RunCountdown = true;
        Task.Run(() =>
        {
            while (_vm.DisplayOnlyShutdownDate > DateTime.Now && _vm.RunCountdown)
            {
                _vm.TimeToShutdown = DateTime.Today + (_vm.DisplayOnlyShutdownDate - DateTime.Now);
                ni.Text = $"Time until {Enum.GetName(typeof(WindowsShutdownMode), _vm.ShutdownMode)}: {_vm.TimeToShutdown.ToString("HH:mm:ss")}";
                int remaining = Convert.ToInt32((_vm.DisplayOnlyShutdownDate - DateTime.Now).TotalSeconds);
                if (remaining == 300)
                {
                    Task.Run(() =>
                    {
                        MessageBox.Show($"5 min remaining");
                    });
                }
                Thread.Sleep(1000);
            }

            if (_vm.RunCountdown)
            {
                InitiateShutdown();
            }

        }
        );

    }

    private void InitiateShutdown()
    {
        _vm.RunCountdown = false;
        //ButtonStop_Click(null, null);
        XmlHelper.SaveConfig(_vm, CONFIGPATH);

        switch (_vm.ShutdownMode)
        {
            case WindowsShutdownMode.Shutdown: System.Diagnostics.Process.Start("shutdown", "/s /t 0"); break;
            case WindowsShutdownMode.Restart: System.Diagnostics.Process.Start("shutdown", "/r /t 0"); break;
            case WindowsShutdownMode.Hibernate: System.Windows.Forms.Application.SetSuspendState(System.Windows.Forms.PowerState.Hibernate, false, false); break;
            case WindowsShutdownMode.Suspend: System.Windows.Forms.Application.SetSuspendState(System.Windows.Forms.PowerState.Suspend, false, false); break;
            default: break;
        }

    }

如前所述,当我最初启动程序时,Ui可以正常工作。单击开始按钮之一时,会出现具有 Visibility =“ {Binding TimeRemainingVisibility} 的网格,单击停止按钮时,它会消失,我可以无数次这样做,但它可以正常工作。 但是,当倒计时完成时,系统将暂停,然后我再次启动它。用户界面(尤其是提到的网格)不再更改。

当我在ViewModel中设置断点时,甚至可以看到在恢复暂停后属性发生的更改,但是Grid保持不可见。再次单击启动计时器,网格也应显示-不会。

这与恢复后显示的“ Windows登录屏幕”有什么关系吗? 如何使它按应有的方式工作? (我知道我可能可以使用BoolToVisibilityConverter代替ViewModel中的两个属性,但我怀疑它会改变这种行为)

另一个问题,实际上并没有改变功能: 最好使用System.Timer每秒更新UI中显示的时间,还是将while。循环与Thread.Sleep(1000)一起使用,为什么?

谢谢您的帮助。

2 个答案:

答案 0 :(得分:0)

首先,永远不要在UI线程中调用Thread.Sleep(),因为它违反了规则并可能被处以死刑,请将其更改为DispatcherTimer。 现在,当计时器即将结束时-将TimeRemainingVisibility更改为可见,然后调用OnPropertyChanged("TimeRemainingVisibility")

答案 1 :(得分:0)

好的,我不知道为什么,但是似乎DataContext在挂起恢复后丢失了。

重新设置DataContext可解决问题:

private void OnPowerChange(object s, PowerModeChangedEventArgs e)
    {
        switch (e.Mode)
        {
            case PowerModes.Resume:
                _vm = XmlHelper.ReadConfig(CONFIGPATH);
                this.DataContext = _vm;
                if (_vm.Dayly)
                {
                    ButtonStart_Click(new Button(), null);
                    if (!ni.Visible)
                    {
                        ni.Visible = true;
                    }
                }
                break;                
            default: break;
        }
    }

还是,有人可以向我解释为什么会这样吗?