我编写了一个程序,可以在一段时间后,特定时间或每天特定时间关闭(关闭,重新启动,休眠,挂起)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)一起使用,为什么?
谢谢您的帮助。
答案 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;
}
}
还是,有人可以向我解释为什么会这样吗?