I have a WPF bar chart that I am updating once every 200ms. It updates with new data fine but there is a ghosting effect. So when a bar changes from 1 to 0.5 there is a faded bar where the original bar was. I'm not sure why this is happening but I would to remove it so the bars move cleanly.
MainWindow.xml
<charting:Chart Grid.Column="0" HorizontalAlignment="Stretch" Margin="6,6,6,6" Name="motorPowerChart" VerticalAlignment="Stretch" Title="Motor Power">
<charting:ColumnSeries IndependentValueBinding="{Binding Key}"
DependentValueBinding="{Binding Value}"
ItemsSource="{Binding}">
</charting:ColumnSeries>
<charting:Chart.Axes>
<charting:LinearAxis Orientation="Y" Minimum="0" Maximum="1" Title="PWM Frequency"/>
</charting:Chart.Axes>
<charting:Chart.LegendStyle>
<Style TargetType="Control">
<Setter Property="Width" Value="0"/>
<Setter Property="Height" Value="0"/>
</Style>
</charting:Chart.LegendStyle>
</charting:Chart>
MainWindow.xml.cs
public partial class MainWindow : Window
{
DispatcherTimer _statusUpdateTimer;
public ObservableCollection<KeyValuePair<string, double>> MotorPowerGraphData = new ObservableCollection<KeyValuePair<string, double>>();
public ObservableCollection<KeyValuePair<string, double>> PIDGraphData = new ObservableCollection<KeyValuePair<string, double>>();
public MainWindow()
{
InitializeComponent();
_statusUpdateTimer = new DispatcherTimer();
_statusUpdateTimer.Interval = new TimeSpan(0, 0, 0, 0, 200);
_statusUpdateTimer.Tick += new EventHandler(updateStatus);
motorPowerChart.DataContext = MotorPowerGraphData;
pidChart.DataContext = PIDGraphData;
}
private void updateStatus(Object myObject, EventArgs myEventArgs)
{
double[] status = SerialCommunications.GetStatus();
MotorPowerGraphData.Clear();
MotorPowerGraphData.Add(new KeyValuePair<String, double>("Motor1", status[0]));
MotorPowerGraphData.Add(new KeyValuePair<String, double>("Motor2", status[1]));
MotorPowerGraphData.Add(new KeyValuePair<String, double>("Motor3", status[2]));
MotorPowerGraphData.Add(new KeyValuePair<String, double>("Motor4", status[3]));
PIDGraphData.Clear();
PIDGraphData.Add(new KeyValuePair<String, double>("Yaw", status[8]));
PIDGraphData.Add(new KeyValuePair<String, double>("Pitch", status[9]));
PIDGraphData.Add(new KeyValuePair<String, double>("Roll", status[10]));
}
Can anyone help me with this?
Thanks
EDIT
I have slowed the timer down so it ticks once per second and have tried inputting dummy data from within the update status function and I am still seeing the same behaviour. It looks worse at 10Hz because the bars dont have time to fade before the next one renders over the top.
Here is my new update status function
int i = 0;
private void updateStatus(Object myObject, EventArgs myEventArgs)
{
/*double[] status = SerialCommunications.GetStatus();
MotorPowerGraphData.Clear();
MotorPowerGraphData.Add(new KeyValuePair<String, double>("M1 " + Math.Round(status[0], 3), status[0]));
MotorPowerGraphData.Add(new KeyValuePair<String, double>("M2 " + Math.Round(status[1], 3), status[1]));
MotorPowerGraphData.Add(new KeyValuePair<String, double>("M3 " + Math.Round(status[2], 3), status[2]));
MotorPowerGraphData.Add(new KeyValuePair<String, double>("M4 " + Math.Round(status[3], 3), status[3]));
PIDGraphData.Clear();
PIDGraphData.Add(new KeyValuePair<String, double>("Yaw " + Math.Round(status[8], 0), status[8]));
PIDGraphData.Add(new KeyValuePair<String, double>("Pitch " + Math.Round(status[9], 0), status[9]));
PIDGraphData.Add(new KeyValuePair<String, double>("Roll " + Math.Round(status[10], 0), status[10]));
IMUGraphData.Clear();
IMUGraphData.Add(new KeyValuePair<String, double>("Yaw " + Math.Round(status[4],0), status[4]));
IMUGraphData.Add(new KeyValuePair<String, double>("Pitch " + Math.Round(status[5], 0), status[5]));
IMUGraphData.Add(new KeyValuePair<String, double>("Roll " + Math.Round(status[6], 0), status[6]));
if (status[7] == 1) batteryStatusLabel.Content = "Battery Level: Charged";
else batteryStatusLabel.Content = "Battery Level: Empty";*/
if (i == 5) i = 0;
else
{
IMUGraphData.Clear();
IMUGraphData.Add(new KeyValuePair<String, double>("Yaw " + i*30, i*30));
IMUGraphData.Add(new KeyValuePair<String, double>("Pitch " + i*-30, i*-30));
IMUGraphData.Add(new KeyValuePair<String, double>("Roll " + i * 15, i * 15));
i++;
}
}
I have uploaded a video to youtube so you can see what the chart looks like as it updates
EDIT
I have tried this in Win forms using the chart control and it works perfectly, however I would like to use WPF because I want to render a cube that rotates in space depending on yaw, pitch and roll and from what I have read this is very easy to do in WPF
EDIT
If no one has an idea why this doesn't work, can anyone recommend some other way of displaying my data?
SOLUTION
I edited the values in place rather than removing and the adding them to the observable collection.
public partial class MainWindow : Window
{
DispatcherTimer _statusUpdateTimer;
public ObservableCollection<KeyValuePair<string, double>> _motorPowerGraphData = new ObservableCollection<KeyValuePair<string, double>>();
public ObservableCollection<KeyValuePair<string, double>> _ratePIDGraphData = new ObservableCollection<KeyValuePair<string, double>>();
public ObservableCollection<KeyValuePair<string, double>> _stabPIDGraphData = new ObservableCollection<KeyValuePair<string, double>>();
public ObservableCollection<KeyValuePair<string, double>> _imuGraphData = new ObservableCollection<KeyValuePair<string, double>>();
public MainWindow()
{
InitializeComponent();
_motorPowerGraphData.Clear();
_motorPowerGraphData.Add(new KeyValuePair<string, double>("M1 0", 0));
_motorPowerGraphData.Add(new KeyValuePair<string, double>("M2 0", 0));
_motorPowerGraphData.Add(new KeyValuePair<string, double>("M3 0", 0));
_motorPowerGraphData.Add(new KeyValuePair<string, double>("M4 0", 0));
_ratePIDGraphData.Clear();
_ratePIDGraphData.Add(new KeyValuePair<String, double>("Yaw 0", 0));
_ratePIDGraphData.Add(new KeyValuePair<String, double>("Pitch 0", 0));
_ratePIDGraphData.Add(new KeyValuePair<String, double>("Roll 0", 0));
_stabPIDGraphData.Clear();
_stabPIDGraphData.Add(new KeyValuePair<String, double>("Yaw 0", 0));
_stabPIDGraphData.Add(new KeyValuePair<String, double>("Pitch 0", 0));
_stabPIDGraphData.Add(new KeyValuePair<String, double>("Roll 0", 0));
_imuGraphData.Clear();
_imuGraphData.Add(new KeyValuePair<String, double>("Yaw 0", 0));
_imuGraphData.Add(new KeyValuePair<String, double>("Pitch 0", 0));
_imuGraphData.Add(new KeyValuePair<String, double>("Roll 0", 0));
_statusUpdateTimer = new DispatcherTimer();
_statusUpdateTimer.Interval = new TimeSpan(0, 0, 0, 0, 200);
_statusUpdateTimer.Tick += new EventHandler(updateStatus);
motorPowerChart.DataContext = _motorPowerGraphData;
ratePIDChart.DataContext = _ratePIDGraphData;
stabPIDChart.DataContext = _stabPIDGraphData;
imuChart.DataContext = _imuGraphData;
}
private void updateStatus(Object myObject, EventArgs myEventArgs)
{
double[] status = SerialCommunications.GetStatus();
_motorPowerGraphData[0] = new KeyValuePair<String, double>("M1 " + Math.Round(status[0], 3), status[0]);
_motorPowerGraphData[1] = new KeyValuePair<String, double>("M2 " + Math.Round(status[1], 3), status[1]);
_motorPowerGraphData[2] = new KeyValuePair<String, double>("M3 " + Math.Round(status[2], 3), status[2]);
_motorPowerGraphData[3] = new KeyValuePair<String, double>("M4 " + Math.Round(status[3], 3), status[3]);
_ratePIDGraphData[0] = new KeyValuePair<String, double>("Yaw " + Math.Round(status[8], 0), status[8]);
_ratePIDGraphData[1] = new KeyValuePair<String, double>("Pitch " + Math.Round(status[9], 0), status[9]);
_ratePIDGraphData[2] = new KeyValuePair<String, double>("Roll " + Math.Round(status[10], 0), status[10]);
_stabPIDGraphData[0] = new KeyValuePair<String, double>("Yaw " + Math.Round(status[8], 0), status[11]);
_stabPIDGraphData[1] = new KeyValuePair<String, double>("Pitch " + Math.Round(status[9], 0), status[12]);
_stabPIDGraphData[2] = new KeyValuePair<String, double>("Roll " + Math.Round(status[10], 0), status[13]);
_imuGraphData[0] = new KeyValuePair<String, double>("Yaw " + Math.Round(status[4],0), status[4]);
_imuGraphData[1] = new KeyValuePair<String, double>("Pitch " + Math.Round(status[5], 0), status[5]);
_imuGraphData[2] = new KeyValuePair<String, double>("Roll " + Math.Round(status[6], 0), status[6]);
if (status[7] == 1) batteryStatusLabel.Content = "Battery Level: Charged";
else batteryStatusLabel.Content = "Battery Level: Empty";
double armed = status[14];
if (armed == 1)
{
armButton.Content = "Disarm";
armedLabel.Content = "Armed: True";
}
else
{
armButton.Content = "Arm";
armedLabel.Content = "Armed: False";
}
double rateMode = status[16];
double stabMode = status[17];
if (rateMode == 1 && stabMode == 0)
{
modeLabel.Content = "Mode: Rate";
modeButton.Content = "Set Mode To Stability";
}
else
{
modeLabel.Content = "Mode: Stability";
modeButton.Content = "Set Mode To Rate";
}
double initialised = status[15];
if (initialised == 1) initialisedLabel.Content = "Initialised: True";
else initialisedLabel.Content = "Initialised: False";
}