07-04-2013 07:52 AM
Hello,
is there any Possibility to bind all Axes and all Plots only in Viewmodel ?
Now i have defined the Axes and Plots in Xaml, but i want to generate it dynamic. Is this possible via the ViewModel ?
My Graph looks now like this:
<ni:Graph x:Name="graph" Grid.Row="0" Margin="10,10,5,5" Grid.Column="0" DataSource="{Binding chartCollection}" PreferIndexData="True" SuppressScaleLayout="True"> <ni:Graph.Axes> <ni:AxisDouble x:Name="xScale" Orientation="Horizontal" Adjuster="ContinuousChart" InteractionMode="None" Visibility="Hidden" Range="0, 600, System.Double"> </ni:AxisDouble> <ni:AxisDouble Orientation="Horizontal" Range="0, 60, System.Double" /> <ni:AxisDouble x:Name="yScale1" Range="0,900" Orientation="Vertical" MinorDivisions="{x:Null}" Adjuster="None" InteractionMode="None" InnerMargin="5" BaselineStroke="{StaticResource YScale1Brush}" > <ni:AxisDouble.MajorDivisions> <ni:RangeLabeledDivisions LabelBrush="{StaticResource YScale1Brush}" /> </ni:AxisDouble.MajorDivisions> </ni:AxisDouble> <ni:AxisDouble x:Name="yScale2" Range="0,900" Orientation="Vertical" MinorDivisions="{x:Null}" Adjuster="None" InteractionMode="None" InnerMargin="10" BaselineStroke="{StaticResource YScale2Brush}" > <ni:AxisDouble.MajorDivisions> <ni:RangeLabeledDivisions LabelBrush="{StaticResource YScale2Brush}" /> </ni:AxisDouble.MajorDivisions> </ni:AxisDouble> <ni:AxisDouble x:Name="yScale3" Range="0, 900, System.Double" Orientation="Vertical" MinorDivisions="{x:Null}" Adjuster="None" InteractionMode="None" Location="Far" InnerMargin="5" BaselineStroke="{StaticResource YScale3Brush}" > <ni:AxisDouble.MajorDivisions> <ni:RangeLabeledDivisions LabelBrush="{StaticResource YScale3Brush}" /> </ni:AxisDouble.MajorDivisions> </ni:AxisDouble> <ni:AxisDouble x:Name="yScale4" Range="0, 900, System.Double" Orientation="Vertical" MinorDivisions="{x:Null}" Adjuster="None" InteractionMode="None" Location="Far" InnerMargin="10" BaselineStroke="{StaticResource YScale4Brush}"> <ni:AxisDouble.MajorDivisions> <ni:RangeLabeledDivisions LabelBrush="{StaticResource YScale4Brush}" /> </ni:AxisDouble.MajorDivisions> </ni:AxisDouble> </ni:Graph.Axes> <ni:Graph.Plots> <ni:Plot VerticalScale="{Binding ElementName=yScale1}" Label="TA1"> <ni:LinePlotRenderer Stroke="{Binding Plot1Color}" StrokeThickness="3" /> </ni:Plot> <ni:Plot VerticalScale="{Binding ElementName=yScale2}" Label="TA2"> <ni:LinePlotRenderer Stroke="Yellow" StrokeThickness="3" /> </ni:Plot> <ni:Plot VerticalScale="{Binding ElementName=yScale3}" Label="NA1"> <ni:LinePlotRenderer Stroke="Blue" StrokeThickness="3" /> </ni:Plot> <ni:Plot VerticalScale="{Binding ElementName=yScale4}" Label="NA2" > <ni:LinePlotRenderer Stroke="Red" StrokeThickness="3" /> </ni:Plot> </ni:Graph.Plots> </ni:Graph>
My ViewModel looks aktually like this:
public ChartCollection<double>[] chartCollection { get; set; } chartCollection = new[] { new ChartCollection<double>(1000), new ChartCollection<double>(1000), new ChartCollection<double>(1000), new ChartCollection<double>(1000), new ChartCollection<double>(1000), new ChartCollection<double>(1000), new ChartCollection<double>(1000), new ChartCollection<double>(1000) }; and data-insert : test = hw.T_a1Actual.ToString(); chartCollection[0].Append(hw.T_a1Actual); chartCollection[1].Append(hw.T_a2Actual); chartCollection[2].Append(hw.n_a1Actual); chartCollection[3].Append(hw.n_a2Actual); }));
But i want to generate all Axes and all Plots dynamic. What is the best Way to do this ?
Can i do this via the ViewModel ?
Thanks in advance
Manuel
Solved! Go to Solution.
07-08-2013 01:27 PM
Keep in mind that the plots and scales on a graph are just components, and not UI objects themselves.
It would be possible, with some code, to take a set of "plot models" and "axis models" and generate plots and axes from them, which could then be synchronized with a graph. If instead you expose the Plot
and AxisDouble
components on the model, then only the synchronization code would be required:
public static class GraphExtensions {
// Private property to hold on to synchronizer.
private static readonly DependencyProperty PlotsSynchronizerProperty =
DependencyProperty.RegisterAttached(
"PlotsSynchronizer", typeof( CollectionViewSynchronizer ), typeof( GraphExtensions ) );
// Public property to declare source of Graph.Plots collection.
public static readonly DependencyProperty PlotsSourceProperty =
DependencyProperty.RegisterAttached(
"PlotsSource", typeof( IEnumerable ), typeof( GraphExtensions ),
new PropertyMetadata( OnPlotsSourceChanged ) );
public static IEnumerable GetPlotsSource( Graph g ) {
return (IEnumerable)g.GetValue( PlotsSourceProperty );
}
public static void SetPlotsSource( Graph g, IEnumerable value ) {
g.SetValue( PlotsSourceProperty, value );
}
private static void OnPlotsSourceChanged( DependencyObject d, DependencyPropertyChangedEventArgs e ) {
var graph = (Graph)d;
// Retrieve existing synchronizer, or create a new one for the target graph.
var synchronizer = (CollectionViewSynchronizer)graph.GetValue( PlotsSynchronizerProperty );
if( synchronizer == null ) {
synchronizer = new CollectionViewSynchronizer { TargetCollection = graph.Plots };
graph.SetValue( PlotsSynchronizerProperty, synchronizer );
}
// Synchronize graph's plots with new source.
synchronizer.SourceCollection = CollectionViewSource.GetDefaultView( e.NewValue );
}
}
In XAML, you could set the attached property on the graph as:
<ni:Graph local:GraphExtensions.PlotsSource="{Binding property on your model}" />
:where the model exposes a collection or observable collection of Plot
objects.
A similar attached property could be defined to synchronize the Axes
collection on a graph with a model collection.
07-09-2013 04:30 AM
This is realy a very great Solution. Thanks a lot.
I've one last Question.
How can i set the VerticalScale of an Plot - object in the ViewModel ?
07-09-2013 07:26 AM - edited 07-09-2013 07:49 AM
Hi,
now i've made the same for the Axis.
Here is my Code
private static readonly DependencyProperty AxesSynchronizerProperty = DependencyProperty.RegisterAttached( "AxesSynchronizer", typeof(CollectionViewSynchronizer), typeof(GraphExtensions)); // Public property to declare source of Graph.Plots collection. public static readonly DependencyProperty AxesSourceProperty = DependencyProperty.RegisterAttached( "AxesSource", typeof(IEnumerable), typeof(GraphExtensions), new PropertyMetadata(OnAxesSourceChanged)); public static IEnumerable GetAxesSource(Graph g) { return (IEnumerable)g.GetValue(AxesSourceProperty); } public static void SetAxesSource(Graph g, IEnumerable value) { g.SetValue(AxesSourceProperty, value); } private static void OnAxesSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var graph = (Graph)d; // Retrieve existing synchronizer, or create a new one for the target graph. var synchronizer = (CollectionViewSynchronizer)graph.GetValue(AxesSynchronizerProperty); if (synchronizer == null) { synchronizer = new CollectionViewSynchronizer { TargetCollection = graph.Axes }; graph.SetValue(AxesSynchronizerProperty, synchronizer); } synchronizer.TargetCollection = graph.Axes; // Synchronize graph's plots with new source. synchronizer.SourceCollection = CollectionViewSource.GetDefaultView(e.NewValue); }
07-09-2013 08:44 AM
Hey, think it doesnt work 😞
Now i get very often the following Exception when i start debugging Visual Studio. Sometimes it works but the most time i got the Exception on Start
System.ArgumentException wurde nicht behandelt. Message=Ein Element mit dem gleichen Schlüssel wurde bereits hinzugefügt. Source=mscorlib StackTrace: bei System.ThrowHelper.ThrowArgumentException(ExceptionResource resource) bei System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add) bei NationalInstruments.Controls.Internal.DataSynchronizer.Receive(IDictionary`2 data) bei NationalInstruments.Controls.Internal.DefaultPipelineDataProcessor.a(DefaultDataItemDescription[] A_0) bei NationalInstruments.Controls.Internal.DefaultPipelineDataProcessor.a() bei System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) bei MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler) bei System.Windows.Threading.DispatcherOperation.InvokeImpl() bei System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state) bei System.Threading.ExecutionContext.runTryCode(Object userData) bei System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData) bei System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) bei System.Windows.Threading.DispatcherOperation.Invoke() bei System.Windows.Threading.Dispatcher.ProcessQueue() bei System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) bei MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) bei MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) bei System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) bei MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler) bei System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs) bei MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) bei MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg) bei System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame) bei System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame) bei System.Windows.Application.RunDispatcher(Object ignore) bei System.Windows.Application.RunInternal(Window window) bei System.Windows.Application.Run(Window window) bei System.Windows.Application.Run() bei MVisu.App.Main() in C:\Projekte\Allgemein\MODAS 3\Modas\MVisu\obj\x86\Debug\App.g.cs:Zeile 0. bei System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) bei System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) bei Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() bei System.Threading.ThreadHelper.ThreadStart_Context(Object state) bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) bei System.Threading.ThreadHelper.ThreadStart() InnerException:
07-09-2013 09:36 AM
The exception you are seeing looks like an issue with how the graph is processing data, rather than anything to do with changes to the Plots
or Axes
collections. Try to reproduce it in a graph without bound collections (and probably post the results as a separate question).
12-11-2017 05:17 AM
Thank you your detailed information. I am new in WPF and I confused about GraphExtensions usage. I am using MVVM pattern.
I have created same code and on my ViewModel and created ObservableCollection<Plot> PlotList list then bind that list to graph as
<ni:Graph local:GraphExtensions.PlotsSource={Binding PlotList} but nothing happens.
I debugged GraphExtensions but SetPlotSource and OnPlotsSourceChanged methods never entered.
What am I missing?
Could you provide small working example for me.
Thanks
Hakan
12-12-2017 02:31 AM
I found answer.
When I changed attached property as DependencyProperty.Register to DependencyProperty.RegisterAttached every thing seems to ok.
Thanks ,
Hakan
08-08-2020 07:54 PM - edited 08-08-2020 07:56 PM
Hello,
My requirement was somewhat similar to this requirement. I tried the solution you have provided and it is working fine with Axes. For plots, I am getting only the first plot line graph plotted. Not able to get where exactly I am going wrong. posting you the screenshot for reference.
This is the datasource I am using. And is updated with new points after each 10 ms.
This is the code when Overlap graph is initialized. (Have used dependency property )
Above is the code to add new point to each list.
Is there any way to Map the axes with the List<Points>?
08-11-2020 11:00 AM
I did not see anything obviously wrong with the code, but it was hard to read in the screenshots. I would suggest you post a new question (with a link back to this question, not a reply here), and if possible attach a small application that reproduces the problem (or at least post the code in a way that can be copied and debugged locally :).