Hi Leon,
I change a little bit your code for demo purposes:
XAML MainWindow:
<Window x:Class="WpfApp1.Window118"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp118"
xmlns:v="clr-namespace:WpfApp1"
mc:Ignorable="d" Top="100" Left="100"
Title="Leon_ng_231106" Height="400" Width="400">
<Window.Resources>
<v:Window118Graph1 x:Key="graph1"/>
<v:Window118Graph1 x:Key="graph2"/>
<local:MainVM x:Key="vm"/>
</Window.Resources>
<Grid DataContext="{StaticResource vm}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ContentControl x:Name="Graph1" Grid.Row="0" Grid.Column="0" local:MainVM.AttProp="True" Content="{StaticResource graph1}"/>
<ContentControl x:Name="Graph2" Grid.Row="0" Grid.Column="1" local:MainVM.AttProp="True" Content="{StaticResource graph2}"/>
<Button Grid.Row="1" Grid.Column="0" Content="Start Graph 1" Command="{Binding}" CommandParameter="Graph1" Margin="5"/>
<Button Grid.Row="1" Grid.Column="1" Content="Start Graph 2" Command="{Binding}" CommandParameter="Graph2" Margin="5"/>
</Grid>
</Window>
XAML UserControl:
<UserControl x:Class="WpfApp1.Window118Graph1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp118"
xmlns:oxyplot="clr-namespace:WpfApp118"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Border>
<oxyplot:PlotView Data="{Binding Points}" />
</Border>
</UserControl>
And all code:
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for Window051.xaml
/// </summary>
public partial class Window118 : Window
{
public Window118()
{
InitializeComponent();
}
}
}
namespace WpfApp118
{
public class MainVM : INotifyPropertyChanged, ICommand
{
private Graph1VM _vm1 = new Graph1VM();
public Graph1VM VM1
{
get { return _vm1; }
set { _vm1 = value; OnPropertyChanged(); }
}
private Graph2VM _vm2 = new Graph2VM();
public Graph2VM VM2
{
get { return _vm2; }
set { _vm2 = value; OnPropertyChanged(); }
}
public static readonly DependencyProperty AttPropProperty = DependencyProperty.Register("AttProp",
typeof(bool), typeof(ContentControl), new UIPropertyMetadata(false, OnAttProp));
public static bool GetAttProp(DependencyObject obj) => (bool)obj.GetValue(AttPropProperty);
public static void SetAttProp(DependencyObject obj, bool value) => obj.SetValue(AttPropProperty, value);
private static void OnAttProp(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
var cc = depObj as ContentControl;
if (cc == null) return;
if ((e.NewValue is bool) && (bool)(e.NewValue))
{
MainVM oldDC = (MainVM)cc.DataContext;
switch (cc.Name)
{
case "Graph1":
cc.DataContext = oldDC.VM1;
break;
case "Graph2":
cc.DataContext = oldDC.VM2;
break;
default:
break;
}
}
}
public void Execute(object parameter)
{
switch (parameter.ToString())
{
case "Graph1":
if (!VM1.worker_updategraph.IsBusy) VM1.worker_updategraph.RunWorkerAsync();
break;
case "Graph2":
if (!VM2.worker_updategraph.IsBusy) VM2.worker_updategraph.RunWorkerAsync();
break;
default:
break;
}
}
public bool CanExecute(object parameter) => true;
SynchronizationContext sc = SynchronizationContext.Current;
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler CanExecuteChanged;
private void OnPropertyChanged([CallerMemberName] string propName = "") =>
sc.Post(new SendOrPostCallback((p) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName))), null);
}
public class Graph1VM : INotifyPropertyChanged
{
public Graph1VM()
{
worker_updategraph = new BackgroundWorker();
worker_updategraph.DoWork += worker_updategraph_DoWork;
}
Random random = new Random();
int time = 0;
internal BackgroundWorker worker_updategraph;
public void worker_updategraph_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
readData();
time++;
System.Threading.Thread.Sleep(50);
}
}
public double[] Points { get; set; } = new double[100];
Random rnd = new Random(100);
public void readData()
{
double[] db = new double[100];
for (int i = 0; i < Points.GetUpperBound(0); i++) db[i] = Points[i + 1];
db[99] = rnd.Next(20, 200);
Points = db;
OnPropertyChanged(nameof(Points));
if (worker_updategraph.IsBusy == false) worker_updategraph.RunWorkerAsync();
}
SynchronizationContext sc = SynchronizationContext.Current;
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler CanExecuteChanged;
private void OnPropertyChanged([CallerMemberName] string propName = "") =>
sc.Post(new SendOrPostCallback((p) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName))), null);
}
public class Graph2VM : INotifyPropertyChanged
{
public Graph2VM()
{
worker_updategraph = new BackgroundWorker();
worker_updategraph.DoWork += worker_updategraph_DoWork;
}
Random random = new Random(200);
int time = 0;
internal BackgroundWorker worker_updategraph;
public void worker_updategraph_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
readData();
time++;
System.Threading.Thread.Sleep(50);
}
}
public double[] Points { get; set; } = new double[100];
Random rnd = new Random();
public void readData()
{
double[] db = new double[100];
for (int i = 0; i < Points.GetUpperBound(0); i++) db[i] = Points[i + 1];
db[99] = rnd.Next(20, 200);
Points = db;
OnPropertyChanged(nameof(Points));
if (worker_updategraph.IsBusy == false) worker_updategraph.RunWorkerAsync();
}
SynchronizationContext sc = SynchronizationContext.Current;
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler CanExecuteChanged;
private void OnPropertyChanged([CallerMemberName] string propName = "") =>
sc.Post(new SendOrPostCallback((p) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName))), null);
}
class PlotView : FrameworkElement
{
VisualCollection children;
Canvas plotArea;
Polyline poly;
Line xAxis, yAxis;
Point Origin = new Point(10, 10);
double[] Points { get; set; } = new double[0];
public PlotView()
{
children = new VisualCollection(this);
initAxis();
initPlotArea();
}
public int XPoints { get; set; } = 100;
public int YPoints { get; set; } = 100;
public Brush LineColor { get; set; } = Brushes.Red;
public static readonly DependencyProperty DataProperty
= DependencyProperty.RegisterAttached("Data", typeof(double[]), typeof(PlotView),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsArrange,
new PropertyChangedCallback(DataChanged)));
public static double[] GetData(DependencyObject obj) => (double[])obj.GetValue(DataProperty);
public static void SetData(DependencyObject obj, double[] data) => obj.SetValue(DataProperty, data);
private static void DataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var plot = d as PlotView;
if (plot == null) throw new InvalidOperationException("Error using Data property.");
var data = e.NewValue as double[];
plot.Points = data;
}
void initAxis()
{
xAxis = new Line() { Stroke = Brushes.LightBlue, StrokeThickness = 2 };
yAxis = new Line() { Stroke = Brushes.LightBlue, StrokeThickness = 2 };
children.Add(xAxis);
children.Add(yAxis);
}
void initPlotArea()
{
plotArea = new Canvas()
{
LayoutTransform = new ScaleTransform() { ScaleY = -1 },
RenderTransform = new TransformGroup() { Children = { new ScaleTransform(), new TranslateTransform() } }
};
children.Add(plotArea);
poly = new Polyline() { Stroke = LineColor, StrokeThickness = 1 };
plotArea.Children.Add(poly);
}
void rearrangeAxis(Size size)
{
xAxis.X1 = 0;
xAxis.X2 = size.Width;
xAxis.Y1 = xAxis.Y2 = size.Height - Origin.Y;
yAxis.X1 = yAxis.X2 = Origin.X;
yAxis.Y1 = 0;
yAxis.Y2 = size.Height;
xAxis.Measure(size);
xAxis.Arrange(new Rect(xAxis.DesiredSize));
yAxis.Measure(size);
yAxis.Arrange(new Rect(yAxis.DesiredSize));
}
void rearrangePlotArea(Size size)
{
plotArea.Width = size.Width - Origin.X;
plotArea.Height = size.Height - Origin.Y;
var translate = (TranslateTransform)((TransformGroup)plotArea.RenderTransform).Children[1];
translate.X = Origin.X;
translate.Y = 0;
plotArea.Measure(size);
plotArea.Arrange(new Rect(plotArea.DesiredSize));
var points = new PointCollection();
double xStep = plotArea.Width / XPoints;
double yFactor = plotArea.Height / YPoints;
for (int i = 0; i < YPoints && i <= Points.GetUpperBound(0); i++)
points.Add(new Point(i * xStep, Points[i]));
poly.Points = points;
}
protected override Size ArrangeOverride(Size finalSize)
{
rearrangeAxis(finalSize);
rearrangePlotArea(finalSize);
return finalSize;
}
protected override Visual GetVisualChild(int index) => children[index];
protected override int VisualChildrenCount => children.Count;
}
}
Result: