Почему представление (видимость) не изменяется после PowerMode.Resume? - PullRequest
0 голосов
/ 04 апреля 2019

Я написал программу, которую можно использовать для выключения (выключения, перезапуска, спящего режима, приостановки) ПК через некоторое время, в определенное время или ежедневно в определенное время.

Когда я запускаюВ приложении в первый раз все (включая вид) работает нормально.Но когда ПК возобновляет работу после приостановки / спящего режима, пользовательский интерфейс, похоже, больше не отвечает.

У меня есть базовая модель:

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

, к которой пользовательский интерфейс привязан:

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

    }

Как упоминалось ранее, пользовательский интерфейс работаетхорошо, когда я изначально запускаю программу.При нажатии на одну из кнопок запуска появляется Сетка, которая имеет Visibility = "{Binding TimeRemainingVisibility} , при нажатии кнопки остановки она исчезает. Я могу делать это бесчисленное количество раз, когда она работает нормально. Однако, когда отсчет времениПосле завершения система завершает работу, и я снова запускаю ее. Пользовательский интерфейс, в частности упомянутая Grid, больше не меняется.

Когда я устанавливаю точки останова в моей модели представления, я даже могу видеть изменения, происходящие со свойствами послеsupend-resume, но Grid остается невидимым. При повторном нажатии на таймеры запуска Grid также должен отображаться - это не так.

Имеет ли это какое-либо отношение к «экрану входа в Windows», которыйотображается после возобновления работы? Как я могу заставить это работать так, как должно?Еще один вопрос, который на самом деле не меняет функциональность: лучше ли намe System.Timer для обновления времени, отображаемого в пользовательском интерфейсе каждую секунду, или для использования цикла while с Thread.Sleep (1000) и почему?

Заранее спасибо за помощь.

Ответы [ 2 ]

0 голосов
/ 04 апреля 2019

Хорошо, я не знаю почему, но, похоже, 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;
        }
    }

Тем не менее, кто-нибудь может мне объяснить, почему это происходит?

0 голосов
/ 04 апреля 2019

Перво-наперво, НИКОГДА не вызывайте Thread.Sleep() в потоке пользовательского интерфейса, это противоречит правилам и наказывается смертью, измените его на DispatcherTimer. теперь, когда таймер заканчивает работу - измените значение TimeRemainingVisibility на видимое и вызовите OnPropertyChanged("TimeRemainingVisibility")

...