1
votes

A ViewModel1 takes data source dynamically from a Model by importing a .csv file containing data values and their distances. The ViewModel1 has two parameterized constructors. It creates a scatter series and stores it in the Series Collection.

private IEnumerable<Series> _myScatterSeries = new Collection<Series>(); 

A View1 is created in response to ViewModel1 using DataTemplates. View1 binds to MyScatterSeries property of ViewModel1 and shows the series representation in scatter chart.
View1.xaml:

<Grid Loaded="Grid_Loaded">
   <ext:ChartExtension Style="{StaticResource ChartStyle1}" SeriesSource="{Binding MyScatterSeries}" />
</Grid>

I want to create a new View window (View2) and it should load the same scatter series created by ViewModel1 dynacamically, when I open View2 window. I tried using the same above code in View2, but it only shows chart, but now series data points. How can I bind MyScatterSeries property of the ViewModel1 to View2 dynamically?

I cannot use the following because it has problem with constructor arguments. View2.xaml-

<UserControl.DataContext>
    <vm:ViewModel1/>
</UserControl.DataContext>

I also tried using DataTemplate, adding stack panel and wrapping the two Views, but it is not working.

As it is a ViewModel-First approach, I have created a new ViewModel (ViewModel2) for View2. But I don't know how to bind MyScatterSeries of ViewModel1 to ViewModel2, using code. Also, Can I use the same code of View1 to show scatter series in View2?

2

2 Answers

3
votes

My first thought is to have one ApplicationViewModel which contains both ViewModel1 and ViewModel2, and takes control of syncing the data.

Here's a very rough example that should give you the general idea

public class ApplicationViewModel
{
    ViewModel1 ViewModel1 { get; set; }
    ViewModel2 ViewModel2 { get; set; }

    public ApplicationViewModel()
    {
        ViewModel1 = new ViewModel1(someParameters);
        ViewModel2 = new ViewModel2(otherParameters);

        ViewModel1.PropertyChanged += VM1_PropertyChanged;
    }

    private VM1_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "ScatterSeries")
            ViewModel2.ScatterSeries = ViewModel1.ScatterSeries;
    }    
}

You could also use this same technique to have both objects point to the same object if you want (no idea how this data is updated or maintained by the user)

public class ApplicationViewModel
{
    ViewModel1 ViewModel1 { get; set; }
    ViewModel2 ViewModel2 { get; set; }

    private IEnumerable<Series> _myScatterSeries;

    public ApplicationViewModel()
    {
        _myScatterSeries = new Collection<Series>(); 

        ViewModel1 = new ViewModel1(someParameters, _myScatterSeries);
        ViewModel2 = new ViewModel2(otherParameters, _myScatterSeries);
    }   
}

The other option would be to use some kind of messaging system to broadcast a message when VM1 data changes, and VM2 would subscribe to those messages and take control of syncing the data. That's a big subject to go into, but I have a quick overview on my blog at Communication between ViewModels with MVVM if you're interested

And last of all, you could just make sure both View1 and View2 have their DataContext set to ViewModel1 assuming there are no other differences. For example,

<DataTemplate x:Key="View1"> 
    <vw:View1/> 
</DataTemplate> 
<DataTemplate x:Key="View2"> 
    <vw:View2 /> 
</DataTemplate>

...

<ContentControl Content="{Binding ViewModel1}" ContentTemplate="{StaticResource View1}" />
<ContentControl Content="{Binding ViewModel1}" ContentTemplate="{StaticResource View2}" />

I don't see how your .DataContext is set in your code above, so can't provide any relevant code sample for that, but it's always an option.

0
votes

You can programmatically set the DataContext for a View in the constructor like this:

//Constructor
public View2()
{
   InitializeComponent();
   DataContext = new ExampleViewModel();
}

You could pass in the ViewModel you want to create to the constructor, or store a global variable which contains the ViewModel.