Hi,
i developing a application where temperature data from a device are shown in a line chart (I use ScatterLine diagram). The x-Axis should show the time values and the y-Axis the temperature values. I use the Microsoft Prism framework. I tried your example 'Binding xamChart to Data' with static values and it works well. But now I wanted to modifiy the data every second by an external thread which throws an exception via the IEventAggregator of the prism framework like i do it with other values which are shown in Textblocks too. To modifiy the data I only add the new data value pair to my existing ObservableCollection but nothing happend.
After this I tried it like described in your 'Modifying the underlaying data' example, I disabled and enabled the RefreshEnabled attribute but know it throws an exception that this is not the right thread ....!
How can I modify the chart by using an eventaggregator?
If I use a viewmodel constructor with a IUnitycontainer parameter your example with the static values did'nt work too. Only if I use a paremeterless constructor it works.
if you are changing properties of the chart from a different thread you need to marshal your interactions onto the UI thread by calling methods on the charts dispatcher. see: http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
As to the first question, you may want to ensure you are updating the observable collection on the UI thread also.
If you can share a small example that illustrates the problem you are encountering, I can give you further pointers.
-Graham
to here es an example code:
1)
UserControl x:Class="ModuleR2.View.ModuleR2View" 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:ig="http://infragistics.com/Chart" xmlns:local="clr-namespace:ModuleR2.ViewModel" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" Background="LightCyan" Height="300" Width="150"> <UserControl.Resources> <ObjectDataProvider x:Key="SampleTemperatureData" ObjectType="{x:Type local:ModuleR2ViewModel}" MethodName="GetTemperatureValues"/> </UserControl.Resources> <StackPanel> <TextBlock FontSize="20" HorizontalAlignment="Center" FontWeight="Bold">Module R2</TextBlock> <Button Margin="5,5,5,5" Command="{Binding Path=ButtonL1Command}">ModuleL1</Button> <ig:XamChart Name="xamChart1" RefreshEnabled="True" Height="150"> <ig:XamChart.Series> <ig:Series Label="Sample" ChartType="ScatterLine" DataSource="{Binding Source={StaticResource SampleTemperatureData}}" DataMapping="ValueX = XValue; ValueY = YValue" StrokeThickness="3" Stroke="Black" Fill="Red"/> </ig:XamChart.Series> </ig:XamChart> </StackPanel></UserControl> 2) here is the viewModel-code which is binded by this.DataContext = viewmodel with the view:namespace ModuleR2.ViewModel{ public class ModuleR2ViewModel : IRegionMemberLifetime, IConfirmNavigationRequest, INotifyPropertyChanged { IRegionManager regionManager; public XamChart xamChart; public ObservableCollection<IXYDoubleValues> SampleTemperatureSeries; public ModuleR2ViewModel(IRegionManager regionManager) { this.regionManager = regionManager; this.ButtonL1Command = new DelegateCommand<object>(this.OnButtonL1Command); SampleTemperatureSeries = new ObservableCollection<IXYDoubleValues>(); IXYDoubleValues xyValue = new XYDoubleValues(0, 20.0); SampleTemperatureSeries.Add(xyValue); xyValue = new XYDoubleValues(1, 25.3); SampleTemperatureSeries.Add(xyValue); xyValue = new XYDoubleValues(2, 29.3); SampleTemperatureSeries.Add(xyValue); } public ModuleR2ViewModel() { SampleTemperatureSeries = new ObservableCollection<IXYDoubleValues>(); IXYDoubleValues xyValue = new XYDoubleValues(0, 20.0); SampleTemperatureSeries.Add(xyValue); xyValue = new XYDoubleValues(1, 25.3); SampleTemperatureSeries.Add(xyValue); xyValue = new XYDoubleValues(2, 29.3); SampleTemperatureSeries.Add(xyValue); container.Resolve<IEventAggregator>().GetEvent<Infrastructure.Services.TopRegion.TopRegionOperatorEvent>().Subscribe(OnTopRegionModuleEvent); } public ObservableCollection<IXYDoubleValues> GetTemperatureValues() { return SampleTemperatureSeries; } public ICommand ButtonL1Command { get; private set; } private void OnButtonL1Command(object obj) { this.xamChart.RefreshEnabled = false; IXYDoubleValues xyValue = new XYDoubleValues(3, 29.3); SampleTemperatureSeries.Add(xyValue); this.xamChart.RefreshEnabled = true; } private void RaisePropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } private void OnTopRegionModuleEvent(Infrastructure.Services.TopRegion.TopRegionOperatorModuleType type) { IXYDoubleValues p = new .XYDoubleValues(SampleTemperatureSeries.Count, 20.0 + SampleTemperatureSeries.Count * 0.1); SampleTemperatureSeries.Add(p); RaisePropertyChanged("GetTemperatureValues"); } } public interface IXYDoubleValues : INotifyPropertyChanged { double XValue { get; set; } double YValue { get; set; } } public class XYDoubleValues : IXYDoubleValues { private double xValue; private double yValue; public XYDoubleValues(double XValue, double YValue) { this.XValue = XValue; this.YValue = YValue; } #region IXYDoubleValues Member public double XValue { get { return xValue; } set { if (value != xValue) { xValue = value; NotifyPropertyChanged("XValue"); } } } public double YValue { get { return yValue; } set { if (yValue != value) { yValue = value; NotifyPropertyChanged("YValue"); } } } #endregion #region INotifyPropertyChanged Member public event PropertyChangedEventHandler PropertyChanged; #endregion private void NotifyPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }}I hopefully added only the necessary code.Now the problems are:1) Why does it only works if I use it implement an parameterless constructor. If I comment the parameterless constructor out no data are available in the chart.2) If I press the 'L1' button the data are added but the chart will not refreshed3) The same like in 2) described happened if an event occured in the 'OnTopRegionModuleEvent' method.Do you need more informations? Hopefully you are able to give me some hints.Best regards!
What is your reasoning behind using ObjectDataProvider? Why not just bind directly to the ObservableCollection on your view model? The problem you are encountering here is that when you use ObjectDataProvider in that manner, it is going to create a new instance of ModuleR2ViewModel different than the one you have assigned to the DataContext, and get the collection from that instance. So it is a different collection than is in your data context.
1) This is because ObjectDataProvider, the way you are using it, is creating a new instance using the parameterless constructor. You can specify arguments to the constructor in ObjectDataProvider, but this doesn't change the fact that it is constructing a new instance (different than the one you assign to this.DataContext). I believe you can set the ObjectInstance property to an existing instance to prevent it from creating a new object instance, but I'm not sure why you need this level of indirection.
2) this is because this is a different instance of the collection than is bound to the chart because of the aforementioned ObjectDataProvider issue.
3) Same as 2.
Hope this helps!
Hi, direct binding works, but is there a possibility to actualize it wihout using a dispatcher?
If you are doing work on a thread other than the UI thread and it needs to update the observable collection, then that interaction will have to be marshaled through the dispatcher. I believe ObservableCollection has thread affinity and will reject updates from a different thread than on which it was created. Could you explain your objections to using the Dispatcher? There are performance concerns, potentially, but the main idea would be to correctly batch your updates to minimize the number of transitions to the UI thread that are required.