pfinferno
pfinferno

Reputation: 1945

Call methods in other windows when viewmodel variable changes

I have a window called Timeline (not my mainwindow), that can be opened several times with different values being passed. What I'm trying to do is, when a certain value is changed in my viewmodel for my main window, I want to call a method in each Timeline window that is open.

Here's the variable that is constantly changing. When it does change, I want to call the testDraw() function (also in the viewmodel).

private int _currentIteration;
public int CurrentIteration //used to hold current index of the list of data fields
{
    get { return _currentIteration; }
    set
    {
        _currentIteration = value;
        this.OnPropertyChanged("CurrentIteration");
    }
}

//Want to call this when CurrentIteration changes
public void testDraw()
{
    foreach (Timeline timeLineWin in Application.Current.Windows)
    {   
        Application.Current.Dispatcher.Invoke(new Action(() =>
        {
            timeLineWin.setCurrentValues(CurrentIteration);
            timeLineWin.linechart1.HighlightPoint(timeLineWin.currentX, timeLineWin.currentY);
        }));
        //linechart1 is a winforms component on the timeline window   
    }            
}

Here's the code behind for the timeline window:

public partial class Timeline : Window
{
    public List<double> xValues;
    public List<double> yValues;
    public double currentX;
    public double currentY;

    public Timeline(List<double> x, List<double> y)
    {
        InitializeComponent();
        xValues = x;
        yValues = y;
    }

    public void setCurrentValues(int i)
    {
        currentX = xValues[i];
        currentY = yValues[i];
    }
}

The function 'testDraw()' is supposed to loop through all the currently open Timeline windows and call the two functions in those windows.

The error I got was:

The calling thread cannot access this object because a different thread owns it. - So I tried using Dispatcher.Invoke in the method which also did not work.

Note: I wasn't sure how to keep this bit in the MVVM format.

Upvotes: 1

Views: 79

Answers (2)

haindl
haindl

Reputation: 3221

You have to invoke your whole testDraw method in the Dispatcher because even the getter of Application.Current.Windows requires it:

public void testDraw()
{
    Application.Current.Dispatcher.Invoke(new Action(() =>
    {
        foreach (Timeline timeLineWin in Application.Current.Windows)
        {
            timeLineWin.setCurrentValues(CurrentIteration);
            timeLineWin.linechart1
                .HighlightPoint(timeLineWin.currentX, timeLineWin.currentY);
        }
    }));
}

Upvotes: 1

openshac
openshac

Reputation: 5165

Have you thought about using the EventAggregator for communicating between windows? There is an option for subscribign on the user interface thread: https://msdn.microsoft.com/en-us/library/ff921122.aspx

When you construct each time line object subscribe to the

eventAggregator.GetEvent<IterationChangeEvent>().Subscribe(UpdateUI, ThreadOption.UIThread);

private void UpdateUI(Iteration iteration)
{
    this.setCurrentValues(iteration);
    this.linechart1.HighlightPoint(this.currentX, this.currentY);
}        

In your main window when the CurrentIteration changes call:

eventAggregator.GetEvent<IterationChangeEvent>().Publish(this.currentIteration);

Your event class would lool something like this:

public class IterationChangeEvent : CompositeWpfEvent<int>
{
}

Upvotes: 1

Related Questions