在 ICommand 操作期间更新标签
Updating a Label during an ICommand Action
在 WPF 中,我使用 MVVM 设计创建了一个屏幕,旨在通过单击按钮将大量日志加载到 ListView
。 return 后,更新标签以显示 returned 的日志数。这个过程有时需要一段时间。我们的 DA 正在进行优化,但同时我需要进行以下更改以向用户表明搜索是 运行:
- 将鼠标显示为
WaitCursor
。
- 更新标签文本以显示“正在搜索...”。
我有一个实现了 ICommand
的 class 并且 WaitCursor
可以正常工作。但是,当搜索为 运行 时,我无法获得更新要显示的标签所需的行为。我当前的代码:
MyScreen.xaml
<Button
Name="DisplayButton"
Content="Display Logs"
Command="{Binding DisplayLogsCommand}"
Margin="0,64,10,0"
VerticalAlignment="Top"
HorizontalAlignment="Right"
Width="112"/>
<Label
Content="{Binding LogsShowingText}"
Margin="0,0,127,8"
Foreground="#FF3C3B3B"
HorizontalAlignment="Right"
Width="145" Height="24"
VerticalAlignment="Bottom"
HorizontalContentAlignment="Right"
FontSize="11"/>
MyScreenVM.cs
private Command displayLogsCommand;
private string logShowingText;
public Command DisplayLogsCommand
{
get
{
if (this.displayLogsCommand == null)
{
// Attempt 3 made here.
bool useWaitCursor = true;
Func<bool> canExecute = () => this.ValidateFields();
Action execute = () =>
{
/*
* TODO: Update this.LogsShowingText to read "Searching..."
*/
// Attempt 1 and 2 made here.
LogEntry[] entries = this.ClientConnection.GetLogs();
this.LogsShowingText = string.Format("Showing {0} Logs", entries.Length);
this.FilteredEntries = new ObservableCollection<LogEntry>(entries);
};
this.displayLogsCommand = new Command(useWaitCursor, canExecute, execute);
}
return this.displayLogsCommand;
}
}
public string LogsShowingText
{
get
{
return this.logsShowingText;
}
set
{
this.logsShowingText= value;
OnPropertyChanged("LogsShowingText");
}
}
到目前为止,结果和我相关的失败尝试如下:
日志 returned 后,标签仅显示为“正在搜索...” .
Application.Current.Dispatcher.Invoke(new Action(() => this.LogsShowingText = "Searching..."));
日志 returned 后,标签仅显示为“显示 N 日志”。
this.LogsShowingText = string.Format("Searching...");
在之前和在搜索期间,标签显示为“正在搜索...",然后 日志被 returned,标签显示为“显示 N 日志”。与 #2 相同的代码,不同的位置。
我知道这可能与 UI 在操作完成之前被阻止有关,这清楚地解释了尝试 1 显示最后排队的标签更新和尝试 2 显示最后的硬编码更新标签。尝试 3 几乎可以工作,但在用户单击按钮执行搜索之前不应更新标签。我该怎么做?
由于这是一个昂贵的命令,意味着 UI 在处理时挂起,您应该将其转换为异步命令。
public Command DisplayLogsCommand
{
get
{
if (this.displayLogsCommand == null)
{
bool useWaitCursor = true;
Func<bool> canExecute = () => this.ValidateFields();
Action execute = async () =>
{
this.LogsShowingText = "Searching";
// Attempt 1 and 2 made here.
LogEntry[] entries = await Task<LogEntry[]>.Run(() => this.ClientConnection.GetLogs());
this.LogsShowingText = string.Format("Showing {0} Logs", entries.Length);
this.FilteredEntries = new ObservableCollection<LogEntry>(entries);
};
this.displayLogsCommand = new Command(useWaitCursor, canExecute, execute);
}
return this.displayLogsCommand;
}
}
通过使 Action
委托异步,您现在可以在 Action
中等待。这使您可以将对 DataLayer 的调用包装在 Task
中并等待它。现在昂贵的操作是 运行 关闭 UI 线程,您的标签将在前后正确更新。无需使用 Dispatcher 编组更改。
这将仅在用户单击按钮执行命令时更新标签。
在 WPF 中,我使用 MVVM 设计创建了一个屏幕,旨在通过单击按钮将大量日志加载到 ListView
。 return 后,更新标签以显示 returned 的日志数。这个过程有时需要一段时间。我们的 DA 正在进行优化,但同时我需要进行以下更改以向用户表明搜索是 运行:
- 将鼠标显示为
WaitCursor
。 - 更新标签文本以显示“正在搜索...”。
我有一个实现了 ICommand
的 class 并且 WaitCursor
可以正常工作。但是,当搜索为 运行 时,我无法获得更新要显示的标签所需的行为。我当前的代码:
MyScreen.xaml
<Button
Name="DisplayButton"
Content="Display Logs"
Command="{Binding DisplayLogsCommand}"
Margin="0,64,10,0"
VerticalAlignment="Top"
HorizontalAlignment="Right"
Width="112"/>
<Label
Content="{Binding LogsShowingText}"
Margin="0,0,127,8"
Foreground="#FF3C3B3B"
HorizontalAlignment="Right"
Width="145" Height="24"
VerticalAlignment="Bottom"
HorizontalContentAlignment="Right"
FontSize="11"/>
MyScreenVM.cs
private Command displayLogsCommand;
private string logShowingText;
public Command DisplayLogsCommand
{
get
{
if (this.displayLogsCommand == null)
{
// Attempt 3 made here.
bool useWaitCursor = true;
Func<bool> canExecute = () => this.ValidateFields();
Action execute = () =>
{
/*
* TODO: Update this.LogsShowingText to read "Searching..."
*/
// Attempt 1 and 2 made here.
LogEntry[] entries = this.ClientConnection.GetLogs();
this.LogsShowingText = string.Format("Showing {0} Logs", entries.Length);
this.FilteredEntries = new ObservableCollection<LogEntry>(entries);
};
this.displayLogsCommand = new Command(useWaitCursor, canExecute, execute);
}
return this.displayLogsCommand;
}
}
public string LogsShowingText
{
get
{
return this.logsShowingText;
}
set
{
this.logsShowingText= value;
OnPropertyChanged("LogsShowingText");
}
}
到目前为止,结果和我相关的失败尝试如下:
日志 returned 后,标签仅显示为“正在搜索...” .
Application.Current.Dispatcher.Invoke(new Action(() => this.LogsShowingText = "Searching..."));
日志 returned 后,标签仅显示为“显示 N 日志”。
this.LogsShowingText = string.Format("Searching...");
在之前和在搜索期间,标签显示为“正在搜索...",然后 日志被 returned,标签显示为“显示 N 日志”。与 #2 相同的代码,不同的位置。
我知道这可能与 UI 在操作完成之前被阻止有关,这清楚地解释了尝试 1 显示最后排队的标签更新和尝试 2 显示最后的硬编码更新标签。尝试 3 几乎可以工作,但在用户单击按钮执行搜索之前不应更新标签。我该怎么做?
由于这是一个昂贵的命令,意味着 UI 在处理时挂起,您应该将其转换为异步命令。
public Command DisplayLogsCommand
{
get
{
if (this.displayLogsCommand == null)
{
bool useWaitCursor = true;
Func<bool> canExecute = () => this.ValidateFields();
Action execute = async () =>
{
this.LogsShowingText = "Searching";
// Attempt 1 and 2 made here.
LogEntry[] entries = await Task<LogEntry[]>.Run(() => this.ClientConnection.GetLogs());
this.LogsShowingText = string.Format("Showing {0} Logs", entries.Length);
this.FilteredEntries = new ObservableCollection<LogEntry>(entries);
};
this.displayLogsCommand = new Command(useWaitCursor, canExecute, execute);
}
return this.displayLogsCommand;
}
}
通过使 Action
委托异步,您现在可以在 Action
中等待。这使您可以将对 DataLayer 的调用包装在 Task
中并等待它。现在昂贵的操作是 运行 关闭 UI 线程,您的标签将在前后正确更新。无需使用 Dispatcher 编组更改。
这将仅在用户单击按钮执行命令时更新标签。