【发布时间】:2011-12-01 18:58:24
【问题描述】:
我有一个 WPF 应用程序,在列表框中显示了几十个用户面板,每个面板代表一个要执行的任务。在每个面板中都会有一个“开始”和“停止”按钮来控制线程。
每个面板都包含一个用于执行的私有后台工作程序,由于它们访问共享资源,因此一次只能执行一个。因此,一旦任务在任何面板中开始,我想禁用每个未运行任务的面板中的按钮,当然,当一切完成时重新启用它们。
所以我想根据 2 个属性启用和禁用: 1.私有后台worker实例变量是否为null 2.公共静态对象是否有锁(使用Monitor.Enter或lock获取)
我想根据以下逻辑启用/禁用按钮:
“开始”按钮: - 如果公共对象未锁定则启用(意味着没有线程在运行),否则禁用(至少一个线程,可能来自此类的线程正在运行)
“停止”按钮 - 如果私有后台工作者不为空(来自此类的线程正在启动/运行),则启用,否则禁用(没有可停止的适用线程)
当一个线程启动时,它将获得共享对象的锁并初始化本地后台工作程序,这将启用单个停止按钮并禁用所有其他启动按钮。
我对 WPF 很陌生,正在研究数据绑定。我可能会弄清楚如何绑定到后台工作者 == 或 != null 但我不确定如何测试对象上是否存在锁以及如何绑定到该对象。
示例: 以下是一些示例代码,跟进下面提供的答案
创建一个带有两个按钮的用户面板(没有为停止按钮实现绑定)
<StackPanel Orientation="Horizontal">
<Button Margin="2" x:Name="btnStart" Content="Start" Click="btnStart_Click" IsEnabled="{Binding CanCommandsExecute}"/>
<Button Margin="2" x:Name="btnStop" Content="Stop"/>
</StackPanel>
将 this 的多个实例放入一个窗口中
<StackPanel Orientation="Vertical">
<wpfsample:TestControl/>
<wpfsample:TestControl/>
<wpfsample:TestControl/>
</StackPanel>
这是 TestControl 的代码隐藏
public partial class TestControl : UserControl, INotifyPropertyChanged
{
private static bool IsLocked = false;
private static object threadlock = new object();
private BackgroundWorker _worker;
public event PropertyChangedEventHandler PropertyChanged;
private bool _canCommandsExecute = true;
public bool CanCommandsExecute {
get { return _canCommandsExecute && (!IsLocked); }
set { _canCommandsExecute = value; OnPropertyChanged("CanCommandsExecute"); } }
public TestControl()
{
DataContext = this;
InitializeComponent();
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
Monitor.Enter(threadlock);
try
{
IsLocked = true;
this.CanCommandsExecute = false;
_worker = new BackgroundWorker();
_worker.DoWork += (x, y) => { Thread.Sleep(5000); };
_worker.RunWorkerCompleted += WorkComplete;
_worker.RunWorkerAsync();
}
catch { Monitor.Exit(threadlock); }
}
private void WorkComplete(object sender, EventArgs e)
{
IsLocked = false;
this.CanCommandsExecute = true;
Monitor.Exit(threadlock);
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
这部分解决了这个问题。当您点击开始时,它会禁用按钮并运行后台任务。它也按照要求使用 WPF 绑定来执行此操作。
悬而未决的问题是如何禁用所有的开始按钮,而不仅仅是一个。我正在锁定一个静态对象(目前无法正常工作,正在调查)
希望这个例子有所帮助
【问题讨论】:
-
没有给出完整的答案,听起来你需要使用数据转换器绑定到对象。这将允许您在代码中而不是 xaml 中进行所需的任何检查。可能与多重绑定。
-
如果要求是在当前任务完成之前不能执行其他任务,出于好奇,您为什么要使用线程?我错过了什么吗?您布置的内容听起来像教科书同步操作 - 用户可以执行一系列任务中的一项,但一次执行一项......
-
@Erik 我正在使用线程,因为启动的任务是长时间运行的进程,不仅仅是一两秒,但在某些情况下长达半小时,我不希望它们在用户界面线程。在任务运行时,您还可以在 UI 中执行其他操作(例如查看另一个先前完成的任务的结果等)。
-
明白了——说得通。鉴于您在那里所说的以及您对 WPF 新手的评论,我发布了回复。
标签: wpf data-binding