这是我在这里发表的第一篇文章,所以我希望我能做好一切。
我正在使用.NET Framework 4 Client Profile。
我想将.doc文件中的数据加载到我的程序中并使用这些信息。这可能需要花费很多时间,因为我需要浏览文档表并查看其中的内容。这已经有效了,这里唯一的问题是屏幕冻结了,你不能看看是否有事情发生。
另外我知道excel会更快更方便,但由于这种类型的数据总是存储在我们公司的word文档中,所以我必须保持这样。
所以我想要做的是计算我必须读取的表中的所有行,将其设置为Progress-Bar的最大值,然后在每行之后计算值+ 1。
我的加载Button
与Command
绑定到LoadWordDocCmd
和进度条:
<Button Name="btnLoadFile"
Content="Load" Height="23"
Command="{Binding LoadWordDocCmd}"
HorizontalAlignment="Right" Margin="0,22,129,0"
VerticalAlignment="Top" Width="50"
Visibility="{Binding VisModeAddNew}"
/>
<ProgressBar HorizontalAlignment="Left" Height="24" Margin="574,52,0,0"
VerticalAlignment="Top" Width="306"
Name="prgBarAddNewLoadWord"
Minimum="0"
Maximum="{Binding AddNewProgressBarMaxVal, Mode=OneWay}"
Value="{Binding AddNewProgressBarValue, Mode=OneWay}"
Visibility="{Binding AddNewProgressBarVisible}"/>
以下是RelayCommand
:
/// <summary>
/// Relaycommand for Function loadWordDocument
/// </summary>
public RelayCommand LoadWordDocCmd
{
get
{
if (this.m_loadWordDocCmd == null)
{
this.m_loadWordDocCmd = new RelayCommand(this.loadWordDocument, canLoadWordDoc);
}
return m_loadWordDocCmd;
}
private set
{
this.m_loadWordDocCmd = value;
}
}
/// <summary>
/// checks if the Word Document can be loaded
/// </summary>
/// <param name="parameter">not used</param>
/// <returns>if it could Execute, then true, else false</returns>
private bool canLoadWordDoc(object parameter)
{
bool ret = false;
if (this.m_fileSelected)
{
ret = true;
}
return ret;
}
我已经做过的是使用BackgroundWorker
。
我能够将Button-Command绑定到RelayCommand
与BackgroundWorker
的函数,但之后我再也无法检查canExecute函数了。
我用它来测试Progress-Bar,它正在运行:
XAML:
<Button ...
Command="{Binding Path=InstigateWorkCommand}"
/>
cs:
private BackgroundWorker worker;
private ICommand instigateWorkCommand;
public ProggressbarSampleViewModel()
{
this.instigateWorkCommand = new
RelayCommand(o => this.worker.RunWorkerAsync(), o => !this.worker.IsBusy);
this.worker = new BackgroundWorker();
this.worker.DoWork += this.DoWork;
this.worker.ProgressChanged += this.ProgressChanged;
}
public ICommand InstigateWorkCommand
{
get { return this.instigateWorkCommand; }
}
private int _currentProgress;
public int CurrentProgress
{
get { return this._currentProgress; }
private set
{
if (this._currentProgress != value)
{
this._currentProgress = value;
OnPropertyChanged("CurrentProgress");
}
}
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.CurrentProgress = e.ProgressPercentage;
}
private void DoWork(object sender, DoWorkEventArgs e)
{
// do time-consuming work here, calling ReportProgress as and when you can
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);
_currentProgress = i;
OnPropertyChanged("CurrentProgress");
}
}
但是如何让它与canExecute一起使用呢?这是我的函数 - 标题:
/// <summary>
/// Function for Load Word Document
/// </summary>
/// <param name="parameter">not used</param>
private void loadWordDocument(object parameter)
这是Relay-Command类:
public class RelayCommand : ICommand
{
private readonly Action<object> methodToExecute;
private readonly Func<object, bool> canExecute;
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
EventHandler handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
public RelayCommand(Action<object> execute)
: this(execute, null) { }
public RelayCommand(Action<object> methodToExecute, Func<object, bool> canExecute)
{
this.methodToExecute = methodToExecute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
// wird keine canExecute-Funktion übergeben, so liefert diese
// true zurück, ansonsten wird die custom canExecute-Funktion
// mit den übergebenen Parametern aufgerufen.
return canExecute == null ? true : canExecute.Invoke(parameter);
}
public void Execute(object parameter)
{
methodToExecute(parameter);
}
}
感谢您的帮助,我希望我能正确发布这个问题!
答案 0 :(得分:1)
我希望我能正确理解你的问题。
GUI应用程序的基本规则是:不要使用GUI线程进行(耗时)数据处理。您必须在后台线程上执行此任务。
由于您使用的是.NET 4.0客户端配置文件,因此您无法使用#include <iostream>
struct barWay {};
struct noBarWay {};
template <int>
struct Bar
{ };
template <typename>
struct selectType
{ typedef noBarWay type; };
template <int N>
struct selectType< Bar<N> >
{ typedef barWay type; };
template <typename T>
struct Foo
{
void foo (noBarWay const &)
{ std::cout << "not Bar version" << std::endl; }
void foo (barWay const &)
{ std::cout << "Bar version" << std::endl; }
void foo ()
{ foo(typename selectType<T>::type()); }
};
int main ()
{
Foo<int> fi;
Foo< Bar<42> > fb;
fi.foo();
fb.foo();
}
/ async
功能。然而,这将是最简单的解决方案。
您可以使用await
来执行此操作。不推荐ThreadPool
。
在您的XAML中,您将BackgroundWorker
属性绑定到ProgressBar.Value
属性,因此我假设您已拥有具有该属性的视图模型。您必须确保更改AddNewProgressBarValue
会引发AddNewProgressBarValue
事件。好消息是,WPF绑定引擎自动将属性值传输操作封送到GUI线程,因此您无需关心哪个线程正在更改您的进度条绑定的属性。
所以解决方案可能看起来像这样(不是生产代码,只是一个想法!):
PropertyChanged
答案 1 :(得分:0)
我认为您的ProggressbarSampleViewModel
代码示例没问题。我测试了它并且它有效。
我假设您要更改LoadWordDocCmd
以获得InstigateWorkCommand
的行为。如果您将ProgressbarSampleViewModel
中的代码放入实际的ViewModel中,那么访问loadWordDocument
和canLoadWordDoc
应该没有问题。另外,正如mm8所述,在DoWork
方法中,您需要调用RaiseCanExecuteChanged
,否则WPF将不会检查CanExecute
方法。
您的ViewModel应该看起来像下面。请参阅大写的评论。
private BackgroundWorker worker;
private RelayCommand instigateWorkCommand; //CHANGE HERE
bool isBusy = false; // ADD THIS
public ProggressbarSampleViewModel()
{
//CHANGE NEXT LINE
this.instigateWorkCommand = new RelayCommand(
o => this.worker.RunWorkerAsync(),
o => !isBusy && canLoadWordDoc(null));
this.worker = new BackgroundWorker();
this.worker.DoWork += this.DoWork;
//REMOVE
//this.worker.ProgressChanged += this.ProgressChanged;
}
public ICommand InstigateWorkCommand
{
get { return this.instigateWorkCommand; }
}
private int _currentProgress;
public int CurrentProgress
{
get { return this._currentProgress; }
private set
{
if (this._currentProgress != value)
{
this._currentProgress = value;
OnPropertyChanged("CurrentProgress");
}
}
}
//REMOVE
//private void ProgressChanged(object sender, ProgressChangedEventArgs e)
//{
// this.CurrentProgress = e.ProgressPercentage;
//}
private void DoWork(object sender, DoWorkEventArgs e)
{
//ADD NEXT LINES
isBusy = true;
Application.Current.Dispatcher.BeginInvoke(
(Action)instigateWorkCommand.RaiseCanExecuteChanged);
// do time-consuming work here, calling ReportProgress as and when you can
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(10);
_currentProgress = i;
OnPropertyChanged("CurrentProgress");
}
//ADD NEXT LINES
isBusy = false;
Application.Current.Dispatcher.BeginInvoke(
(Action)instigateWorkCommand.RaiseCanExecuteChanged);
}
bool m_fileSelected = true; //CHANGE TO SEE THE EFFECT
//REMOVE
//RelayCommand m_loadWordDocCmd;
///// <summary>
///// Relaycommand for Function loadWordDocument
///// </summary>
//public RelayCommand LoadWordDocCmd
//{
// get
// {
// if (this.m_loadWordDocCmd == null)
// {
// this.m_loadWordDocCmd = new RelayCommand(this.loadWordDocument, canLoadWordDoc);
// }
// return m_loadWordDocCmd;
// }
// private set
// {
// this.m_loadWordDocCmd = value;
// }
//}
/// <summary>
/// checks if the Word Document can be loaded
/// </summary>
/// <param name="parameter">not used</param>
/// <returns>if it could Execute, then true, else false</returns>
private bool canLoadWordDoc(object parameter)
{
bool ret = false;
if (this.m_fileSelected)
{
ret = true;
}
return ret;
}
/// <summary>
/// Function for Load Word Document
/// </summary>
/// <param name="parameter">not used</param>
private void loadWordDocument(object parameter)
{
}
希望这有帮助。