【发布时间】:2014-04-10 20:39:59
【问题描述】:
我正在尝试设置由 WCF 回调线程更新的依赖项属性。
MainWindow.xaml 上有一个 ProgressBar 绑定到此属性:
MainWindow.xaml
<ProgressBar Name="ProgressBar" Value="{Binding Progress, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
MainWindow有一个DemoModule的实例,定义为:
DemoModule.xaml.cs
/// <summary>
/// Interaction logic for DemoModule.xaml
/// </summary>
public partial class DemoModule : UserControl, INotifyPropertyChanged
{
public static readonly DependencyProperty ProgressProperty = DependencyProperty.Register("Progress", typeof(int), typeof(DemoModule));
public event ProgressEventHandler ProgressChanged;
public event PropertyChangedEventHandler PropertyChanged;
public int Progress
{
get { return (int)GetValue(ProgressProperty); }
set { SetValue(ProgressProperty, value); } // setter throws InvalidOperationException "The calling thread cannot access this object because a different thread owns it"
}
/// <summary>
/// Initializes a new instance of the <see cref="DemoModule" /> class.
/// </summary>
public DemoModule()
{
InitializeComponent();
ProgressChanged += OnProgressChanged;
}
/// <summary>
/// Called when [progress changed].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="args">The <see cref="ProgressChangedEventArgs" /> instance containing the event data.</param>
public void OnProgressChanged(object sender, ProgressChangedEventArgs args)
{
Debug.WriteLine("Current Thread: {0}", Thread.CurrentThread.ManagedThreadId);
Debug.WriteLine("Current Dispatcher Thread: {0}", Application.Current.Dispatcher.Thread.ManagedThreadId);
if (ProgressChanged == null) return;
Debug.WriteLine("ProgressChangedEventArgs.Current: " + args.Current);
Progress = Convert.ToInt32(args.Current * 100);
OnPropertyChanged("Progress");
}
/// <summary>
/// Called when [property changed].
/// </summary>
/// <param name="propertyName">Name of the property.</param>
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
Trace.WriteLine("Property " + propertyName + " changed. Value = " + Progress);
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Progress.set() 由于线程关联性而引发异常。
我该如何解决这个问题?
更新 1
据称这是线程安全的,但没有效果:
public int Progress
{
get
{
return Dispatcher.Invoke((() => (int)GetValue(ProgressProperty)));
}
set
{
Dispatcher.BeginInvoke((Action)(() => SetValue(ProgressProperty, value)));
}
}
更新 2
我的 DemoModule.xaml.cs 有一个对实现 WCF 回调方法 OnUpdateProgress 的客户端库的引用:
InstallerAgentServiceClient.cs
public void OnUpdateProgress(double progress)
{
//Debug.WriteLine("Progress: " + progress*100 + "%");
var args = new ProgressChangedEventArgs(progress, 1, "Installing");
_installModule.OnProgressChanged(this, args);
}
上面的_installModule 对象是DemoModule 的实例。
更新 3
从 WCF 客户端库中删除 [CallBackBehavior] 属性后,似乎不再存在线程同步问题。我可以按如下方式更新 MainWindow 中的进度条:
DemoModule.xaml.cs
public void OnProgressChanged(object sender, ProgressChangedEventArgs args)
{
Progress = Convert.ToInt32(args.Current * 100);
var progressBar = Application.Current.MainWindow.FindName("ProgressBar") as ProgressBar;
if (progressBar != null)
progressBar.Value = Progress;
}
【问题讨论】:
-
为什么要实现 INotifyPropertyChanged?如果您绑定到 DependencyProperties,则不需要它。
-
我认为我必须同时做这两件事。还是在这里学习。我使用 WPF 的第一周。
-
实际上,VM 实现有两种可能性。第一个是实现 INotifyPropertyChanged 接口。第二种是从DependencyObject 继承,并将属性声明为Dependency 属性。第一个通常是最合适的。然而,ViewModel 不应该从像 UserControl 这样的 WPF 控件继承......好吧,除了某些特定情况。
-
既然我们不能做多重继承,我想我将不得不坚持使用 INPC。
-
显示您的 WCF 调用以更新进度条。
标签: c# wpf wcf wpf-controls wpf-4.0