Sunday, April 29, 2007 7:54 PM
Tamir Khason
Dependency property getters and setters in multithreaded environment
Recently I blogged about Thread safe ObservableCollection. And what's about regular single Dependency Properties? Actually, the story is rather same and the rule is clear - Bring into your STAThread first, then do whatever you want to do. So how to do it? Simple Let's create regular window with two DPs MyTextProperty and ButtonTitleProperty. We'll bind TextBox to MyTextProperty and will change it from other thread, based on ButtonTitleProperty value.
So, first of all those properties
public static readonly DependencyProperty ButtonTitleProperty = DependencyProperty.Register("ButtonTitle", typeof(ButtonStates), typeof(Window1), new UIPropertyMetadata(ButtonStates.Start)); public static readonly DependencyProperty MyTextProperty = DependencyProperty.Register("MyText", typeof(string), typeof(Window1));
Now, the crazy thread
Random r;
void updateText()
{
r = new Random();
while (ButtonTitle == ButtonStates.Stop)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10; i++)
{
sb.Append((char)r.Next(0x41, 0x51));
}
MyText = sb.ToString();
}
}
And finally the method to invoke it
void onClick(object sender, RoutedEventArgs e)
{
if (ButtonTitle == ButtonStates.Start)
{
//start
new Thread(new ThreadStart(updateText)).Start();
ButtonTitle = ButtonStates.Stop;
}
else
{
//end
ButtonTitle = ButtonStates.Start;
}
}
<Button Click="onClick" Content="{Binding ElementName=myWindow, Path=ButtonTitle}"/>
<TextBlock Text="{Binding ElementName=myWindow, Path=MyText}"/>
Now, try to run - the first thing will happen is exception: The calling thread cannot access this object because a different thread owns it. in ButtonTitle getter The next - the same exception in MyText setter. What to do? Bring the value into STAThread. How? Simple instead of regular getter
get { return (ButtonStates)GetValue(ButtonTitleProperty); }
We'll use thread safe getter
get
{
try
{
return (ButtonStates)this.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Background,
(DispatcherOperationCallback)delegate { return GetValue(ButtonTitleProperty); },
ButtonTitleProperty);
}
catch
{
return (ButtonStates)ButtonTitleProperty.DefaultMetadata.DefaultValue;
}
}
And instead of regular setter
set { SetValue(MyTextProperty, value); }
We'll use thread safe setter
set
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Background,
(SendOrPostCallback)delegate { SetValue(MyTextProperty, value); },
value);
}
I believe it's clear why getter do synchronous and setter does not? :)
Try to experiment with DispatcherPriority enum to see how busy your system will be. Good luck
Source code for this article
תגים:WPF, tutorial, Tips and Tricks, source