Jed I
Jed I

Reputation: 1018

changing ellipse colour at runtime

I have not used wpf alot and thought it would be a simple proccess to change the colour of an ellipse at runtime. I have a FileWatcher and in the event created I want to change the colour of the ellipse to a colour and back again, creating a blinking effect. (created is the ellipse, br4 is a solid colour brush defined in xaml)

    public void watcherCreated(object seneder, FileSystemEventArgs e)
    {

        Application.Current.Resources["br4"] = new SolidColorBrush(Colors.Green);
        created.Fill = (SolidColorBrush)Application.Current.Resources["br4"];

    }

as soon as a file is created in the path which fires the event i get this error: Invalid operation exception The calling thread cannot access this object because a different thread owns it. I have looked for a solution with the freeze() method, but with no success.

     created.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(
            delegate()
            {
                Application.Current.Resources["br4"] = new SolidColorBrush(Colors.Green);
                created.Fill = (SolidColorBrush)Application.Current.Resources["br4"];
            }
        ));

got it thanks for comments

Upvotes: 0

Views: 2514

Answers (3)

Simsons
Simsons

Reputation: 12735

In WPF all the UI controls are loaded in a different thread while as your application runs in a separate thread.

So , think it as , you are getting this error because your application(Main Thread) is trying to access the Elipse that is in UIThread. And this is not allowed for as threads can not access each others object directly.

So WPF has introduced dispatcher object. Use the following

if (this.Dispatcher.Thread != System.Threading.Thread.CurrentThread)
{
    this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
        new Action(
            delegate()
            {
                Application.Current.Resources["br4"] = new SolidColorBrush(Colors.Green);
                created.Fill = (SolidColorBrush)Application.Current.Resources["br4"];
            }
            ));
}

Upvotes: 1

Colin Smith
Colin Smith

Reputation: 12540

You can only access UI elements from the same thread in which they were created.

You should use Dispatcher.Invoke or Dispatcher.BeginInvoke to have a delegate called on the UI thread...where you can then access the "created" elements' "Fill" property.

See this link for an explanation of the problem:

Instead of trying to set the changing colour in the UI...what you can do is expose a property on your ViewModel which holds a state.

When your FileWatcher notifies you of a newly created file (by calling your watcherCreated method) you just set that state in your ViewModel.

In your UI...use a Binding with a Converter to bind to the state property in your ViewModel. The converter will determine what Brush to use depending on the state e.g. if state is 1 return a Green brush, if state is 0 return a Red brush.

To reset the state back to the "off" position...you could have a timer that after say 1 second, etc...sets the state value back to off.

By doing this....you separate the state from the UI.

If in the future you wanted a more sophisticated way of showing the state in the UI...e.g. having an animation (using StoryBoards/Visual State Manager) that gradually fades away from Green back to Red...then you could have that animation triggered, once again based on the state in the ViewModel.

Upvotes: 1

neo
neo

Reputation: 425

Even simpler solution is to set the created.Fill on the UI thread itself. you will not need Dispatcher.Invoke or Dispatcher.BeginInvoke.

Upvotes: 0

Related Questions