SciChart® the market leader in Fast WPF Charts, WPF 3D Charts, and iOS Chart & Android Chart Components

4 votes

Hi there, and thanks for your enquiry!

It just so happens we have a sample for this already in our internal test-suite. Please see the attached.

What it does is append LabelAnnotations for the values on a Histogram plot. The solution is quite simple, it just uses this code to ensure label annotations are placed centred and above columns:

private void HistogramExampleView_Loaded(object sender, System.Windows.RoutedEventArgs e)
{            
    var series = new XyDataSeries<double, double>();
    series.SeriesName = "Histogram";

    var yValues = new double[] { 0.0, 0.1, 0.2, 0.4, 0.8, 1.1, 1.5, 2.4, 4.6, 8.1, 11.7, 14.4, 16.0, 13.7, 10.1, 6.4, 3.5, 2.5, 1.4, 0.4, 0.1, 0.0, 0.0 };

    for (int i = 0; i < yValues.Length; i++ )
    {
        // DataSeries for appending data
        series.Append(i, yValues[i]);   
 
        // Annotations for text labels
        var labelAnnotation = new TextAnnotation()
                                    {
                                        FontSize = 10,
                                        X1 = i,
                                        Y1 = yValues[i],
                                        Text = yValues[i].ToString(),
                                        Foreground = new SolidColorBrush(Color.FromArgb(0xFF,0x33,0x33,0x33)),
                                        FontWeight = FontWeights.Bold,
                                        VerticalAnchorPoint = VerticalAnchorPoint.Bottom,
                                        HorizontalAnchorPoint = HorizontalAnchorPoint.Center
                                    };

        this.sciChart.Annotations.Add(labelAnnotation);
    }

    columnSeries.DataSeries = series;
}

If you wanted to do something more complex, maybe dynamically place annotations based on changing data, then you could look at this post, which describes an architecture for managing and keeping track of annotations using custom chartmodifiers.

Andrew

4 votes

Hi there,

Please see our KB article on How to Add Mouse Interaction to SciChart, the section on ChartModifier Precedence and Handled mouse events.

ChartModifier Precedence (Handled Events)

ChartModifiers obey a precedence, rather like WPF RoutedEvents. If you have a number of modifiers in a SciChartSurface, then the first modifier that handles an event marks it as e.Handled. Subsequent modifiers will not receive the event.

For instance, consider the following code:


    
        
        
             
             
             
             
        
    

If you drag the chart, then the series will not be selected too, because the RubberBandXyZoomModiifer marks MouseUp as Handled. The SeriesSelectionModifier relies on MouseUp to select. If the event is handled then it will not select.

So far this is all intuitive and good. But what about this case?


    
        
        
             
             
             
             
        
    

Working around event handling with ReceiveHandledEvents

The solution to the above problem is to set ReceiveHandledEvents=True on a modifier. This way the modifier will receive all events, even those marked as handled. Try the following code for instance:


    
        
        
             
             
             
             
        
    

Be careful with this flag though as it could enable event handling where it should not occur.

4 votes

SciChart v3.0 now allows detection of whether VisibleRangeChanged was fired during animation or not. Try this code below:

public void Foo()
{
    var axis = new NumericAxis();

    axis.VisibleRangeChanged += (s, e) =>
        {
            if (e.IsAnimating)
            {
                // VisibleRangeChanged occurred during animation                        
            }
            else
            {
                // VisibleRangeChanged did not occur during animation
            }
        };
}
4 votes

Hi Manish,

I’ve just investigated this now – you’re absolutely right! The above code doesn’t work on the axes. Here. Try this – its a custom modifier I’ve developed which demonstrates hit testing of the YAxis and XAxis.

This code lets you detect click on axis.

public class HitTestingModifier : ChartModifierBase
{
    public override void OnModifierMouseDown(ModifierMouseArgs e)
    {
        bool isOnChart = IsPointWithinBounds(e.MousePoint, ModifierSurface);
        bool isOnYAxis = IsPointWithinBounds(e.MousePoint, YAxis);
        bool isOnXAxis = IsPointWithinBounds(e.MousePoint, XAxis);

        MessageBox.Show(string.Format("Clicked YAxis? {0}\nClicked XAxis? {1}\nClicked Chart? {2}", isOnYAxis,
                                        isOnXAxis, isOnChart));

        base.OnModifierMouseDown(e);
    }

    public bool IsPointWithinBounds(Point point, IHitTestable element)
    {
        var tPoint = ParentSurface.RootGrid.TranslatePoint(point, element);

        bool withinBounds = (tPoint.X <= (element as FrameworkElement).ActualWidth && tPoint.X >= 0)
                            && (tPoint.Y <= (element as FrameworkElement).ActualHeight && tPoint.Y >= 0);

        return withinBounds;
    }
}

If you click in the XAxis, YAxis or chart region you will get a message box showing what element has been clicked.

Example attached

Thanks!

  • Andrew answered 7 years ago
  • last active 1 year ago
4 votes

(I’m fairly new with SciChart myself, so I’m not sure if this is the best way to do it…)
You can look at what I’m building for a start. (Functional, but no comments yet. Hopefully the names are obvious enough)
I’m sure there will be a few extensions missing, but hopefully it’ll be obvious what they do.

When you add an annotation, it will show the textbox and tail pointers at an offset from the referenced data point.
When you zoom in and out and pan around, the annotation components will retain their relative size and offset to the point.
In edit mode, you can drag the text and reference points to new locations.

Here are two of the main extensions I know of:

    public static void AddHandler(this DependencyProperty prop, object component, EventHandler handler)
    {
        DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(prop, component.GetType());
        if (dpd != null)
            dpd.AddValueChanged(component, handler);
    }
    public static void AddHandler(this object component, DependencyProperty prop, EventHandler handler)
    {
        DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(prop, component.GetType());
        if (dpd != null)
            dpd.AddValueChanged(component, handler);
    }
4 votes

Hello there,

I’m investigating this now.

I can confirm if I run the application on my pc, while it doesn’t lock up by 16,000 points, it is certainly slower. What hardware do you have out of interest? It shouldn’t make much difference (unless its really slow). Mine is quad-core i7. 2.4GHz, so nothing stellar, but no sluggard either.

(BTW I’m really impressed with the SciChart application you’ve written in such a short period of time as in the trial. Nice application & really impressive job!)

Now, I ran your test application through DotTrace. The results are attached.

They make interesting reading! Basically the SciChartOverview is responsible for 40% of the time spent on the main thread. Particularly where the overview control is zooming itself to extents on each point added (on each redraw). We haven’t really optimized the overview for performance as it never raised itself as a bottleneck – it has now!

So, my first piece of advice is, remove the overview

The next interesting thing is that the axis drawing takes 10% of the time. In particular, Axis tick-drawing takes 6.4%. Remember if we remove overview all these figures double, so the tick drawing alone is quite significant.

My second recommendation is to Set AxisBase.DrawMinorTicks = false on all XAxes and YAxes

This will reduce the number of lines these are adding to the screen per frame. Axis ticks and labels are drawn using WPF canvases which are far slower than bitmap renderered series, so we want to minimise these if at all possible. If you can handle it setting AxisBase.DrawMinorGridLines = false will further improve performance since it will reduce the number of elements on the screen, however, there is only one set of gridlines for N axes and there are N sets of minor ticks, so minor ticks are the low hanging fruit here.

My third recommendation is to set SciChartSurface.RenderPriority = RenderPriority.Low

What does this do? It puts the priority of the render thread below the priority of mouse and keyboard input. This is really important as once the UI thread approaches saturation you will lose keyboard and mouse interactivity. This will really annoy your users and give you the impression that the application is ‘locked up’. By using priority low for rendering you give control back to the message loop to process mouse and key events and make your application a lot happier. The trade off is the chart rendering may appear more stuttery but it is because redraws are postponed slightly while the WPF message loop is flushed, resulting in a more fluid application experience.

If you want you can go one further and set RenderPriority.Manual. Then you are responsible for calling SciChartSurface.InvalidateElement() after appending points. For instance, you could set a timer and call InvalidateElement() every 1/10th of a second, rather than for each point appended.

My final recommendation is to throttle data appended to the chart, using the DataSeries.Append(IEnumerable<TX> xValues, IEnumerable<TY> yValues) API

The chart dataseries already do a good job of throttling data. For instance, there is not necessarily 1 redraw for each point appended. However, on each point appended some calculations are performed, such as Min Max calculations of the DataSeries, and checking internal state of the chart before pushing a redraw message onto a stack for the renderer thread to pick up. This is a small overhead but if you are calling DataSeries.Append in a tight loop it will soon add up. You can buffer or throttle appends but using the DataSeries.Append(IEnumerable<TX> xValues, IEnumerable<TY> yValues) method. Passing in arrays here of even size 10 or 100 points at a time can significantly improve throughput.


I’ve just tried the above (except for buffering Appends since that would require bigger changes to the code than I can do here) and I’m seeing task manager report a more healthy 7% CPU at ~16k points rather than the 29% I was seeing before. Can you try these and see if you get the same?

Regarding the overview, we’ll work on that. I suggest leaving it out for now and replacing with a scrollbar, calculating the offset based on XAxis.VisibleRange vs. the known DataSeries.XValues

Best regards,
Andrew

4 votes

Update June 2014: The Short Answer

We’ve now integrated the X and Y Values to the Rollovermodifier natively, and this is demonstrated in our examples. Please see the Realtime Cursors Example which demonstrates a Rollover with X and Y Values in the Legend area.

The Long Answer

The way it works is like this. The RolloverModifier and LegendModifier are simply data-sources, which give you an ObservableCollection<SeriesInfo> to bind to in XAML.

The Series Info classes are defined in the API documentation here:

Each Series has its own SeriesInfo type. All of them inherit from SeriesInfo, which is the base type. So given you are binding to a collection of SeriesInfo when you use the RolloverModifier or LegendModifier, it becomes possible to expose almost any info about the underlying RenderableSeries or DataSeries.

As a starting point please see our RolloverModifier demo, in particular the source code where we create the ItemsControl to consume SeriesInfo objects:

<!-- Binds to SeriesInfo, outputs Y-Values only -->
<!-- By modifying this and using knowledge of SeriesInfo class definition above, -->
<!-- you can theoretically do anything! --> 
<Border Grid.Row="1" Margin="23,23" HorizontalAlignment="Left" VerticalAlignment="Top" Background="#77FFFFFF" BorderBrush="#55000000" BorderThickness="2" Padding="5">
    <ItemsControl DataContext="{Binding ElementName=rolloverModifier}" ItemsSource="{Binding RolloverData.SeriesInfo}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>

                    <TextBlock Grid.Column="0"
                               Margin="3,3,20,3"
                               FontSize="13"
                               FontWeight="Bold"
                               Foreground="{Binding SeriesColor,
                                                    Converter={StaticResource ColorToBrushConverter}}"
                               Text="{Binding SeriesName}" />
                    <TextBlock Grid.Column="1"
                               Margin="3,3,3,3"
                               FontSize="13"
                               FontWeight="Bold"
                               Foreground="{Binding SeriesColor,
                                                    Converter={StaticResource ColorToBrushConverter}}"
                               Text="{Binding Value}" />
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Border>

Note that when you bind to XValue / YValue which are of type IComparable, you will need a converter or a StringFormat to convert from IComparable to the display value. Yes believe it or not, in XAML if you bind an IComparable which is actually a double to a TextBlock it won’t display! Please use an IComparable converter if you get blank values when binding to these properties.

In the RealTimeCursors example we see this code to convert the XValue, YValue to doubles:

<!-- When binding to XValue, YValue of type IComparable, StringFormat is mandatory due to a -->
<!-- XAML bug that cannot convert IComparable to text, even though underlying type is double -->
<StackPanel Orientation="Horizontal" Grid.Column="2">
    <TextBlock Text="X: " Style="{StaticResource tbStyle}"/>
    <TextBlock Text="{Binding XValue, StringFormat=\{0:0.00\}}" Style="{StaticResource tbStyle}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Column="3">
    <TextBlock Text="Y: " Margin="3" Style="{StaticResource tbStyle}"/>
    <TextBlock Text="{Binding YValue, StringFormat=\{0:0.00\}}" Style="{StaticResource tbStyle}"/>
</StackPanel>

Advanced SeriesInfo Binding

Finally, many people ask us how to bind to a specific property on the RenderableSeries or DataSeries. Well, the SeriesInfo exposes the RenderableSeries too so its possible to expose any property from the RenderableSeries. Don’t forget RenderableSeries.DataSeries also allows access from SeriesInfo right back to the original DataSeries.

  • Andrew
  • Andrew answered 5 years ago
  • last active 5 years ago
4 votes

Thanks to Andrew, setting the ResamplingMode to Auto seems to have fixed the problem

  • kewur answered 5 years ago
4 votes
In reply to: Documentation

Hello Catalin,

Yes there is. All of our documentation is now online. Please see this related question: Where is the Documentation.

We also have a demo on Xaml styling in the examples suite, Please see our WPF Chart Xaml Styling Example

  • Andrew answered 7 years ago
  • last active 3 years ago
4 votes
4 votes

Hello Joerg,

Axis Label styling was introduced in SciChart v3.0. You can now set a Style for a label including margins, LayoutTransforms etc…

Please see the answer on Styling Axis Labels for more information.

Best regards,
Yuriy

  • Yuriy answered 7 years ago
4 votes

Hello Joerg,

As part of SciChart v3.0 all axis types now show labels at VisibleRange.Max and Min, so long as VisibleRange.Max, Min fall at ‘nice’ values, e.g. 1.0, 1.5, 2.0, 2.5 etc… By default the chart will choose these nice values when first showing or autoranging.

When you scroll however, the labels scroll too – you lose the labels at the edges of the axis. In this case you might want to use our new StaticAxis Feature, which fixes axis labels at fixed intervals and updates the label value instead.

Best regards,
Yuriy

  • Yuriy answered 7 years ago
  • last active 3 years ago
4 votes

Hi George,

This is a problem we’re trying to solve (MVVM in general) and there’s no good answer at the moment! Just a note, there may be some changes in the SeriesSource API in the next major release (quite a while off though) to make this easier.

The problem is we need to separate view & viewmodel, yet retain control over series-type, colors, point-markers from the viewmodel which are much better defined in the XAML.

If you wanted to apply the same RolloverMarker to all series, you could use an unnamed style, e.g.

<!-- Define in UserControl.Resources, or Application resources, with no key -->
<Style TargetType="{x:Type SciChart:FastLineRenderableSeries}">
            <Setter Property="RolloverMarkerTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <Ellipse Width="7" Height="7" Fill="SlateGray" Stroke="SlateGray" StrokeThickness="1" />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

This will apply the same Rollover Marker to all FastLineRenderableSeries’ in that scope.

Perhaps another solution is to define a ResourceDictionary which contains your styles (this time keyed) and set them using an attached property? E.g. if a resource dictionary was added to the App.xaml you can retrieve styles and templates from it by calling Application.Current.Resources[“key”]. Rather than getting application resources in your viewmodel sometime’s it’s better to define a property in your viewmodel of type string, and use attached properties to apply the style.

For example:

        <!-- TODO: ControlTemplates can be defined in resource dictionaries and added to App.Xaml -->
        <ControlTemplate x:Key="GrayMarkerTemplate">
            <Ellipse Width="7" Height="7" Fill="SlateGray" Stroke="SlateGray" StrokeThickness="1" />
        </ControlTemplate>

        <ControlTemplate x:Key="BlueMarkerTemplate">
            <Ellipse Width="7" Height="7" Fill="SteelBlue" Stroke="SteelBlue" StrokeThickness="1" />
        </ControlTemplate>
// Create attached properties and attached behaviours to assist and work around MVVM's limitations
public class RolloverMarkerHelper : DependencyObject
    {
        public static readonly DependencyProperty RolloverTemplateKeyProperty =
            DependencyProperty.RegisterAttached("StyleKey", typeof (string), typeof (RolloverMarkerHelper), new PropertyMetadata(default(string), OnRolloverTemplateKeyChanged));

        public static void SetRolloverTemplateKey(UIElement element, string value)
        {
            element.SetValue(RolloverTemplateKeyProperty, value);
        }

        public static string GetRolloverTemplateKey(UIElement element)
        {
            return (string) element.GetValue(RolloverTemplateKeyProperty);
        }

        private static void OnRolloverTemplateKeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var element = d as BaseRenderableSeries;

            if (element == null)
                return;

            if (e.NewValue == null)
            {
                element.RolloverMarkerTemplate = null;
                return;
            }

            if (Application.Current != null)
            {
                var resource = Application.Current.TryFindResource(e.NewValue) as ControlTemplate;
                element.RolloverMarkerTemplate = resource;
            }
        }
    }

Then the template can be applied like this

            _chartSeries = new ObservableCollection<IChartSeriesViewModel>();

            var rSeries0 = new FastLineRenderableSeries();
            RolloverMarkerHelper.SetRolloverTemplateKey(rSeries0, "GrayMarkerTemplate");
            _chartSeries.Add(new ChartSeriesViewModel(ds0, rSeries0));

            var rSeries1 = new FastLineRenderableSeries();
            RolloverMarkerHelper.SetRolloverTemplateKey(rSeries1, "BlueMarkerTemplate");
            _chartSeries.Add(new ChartSeriesViewModel(ds1, rSeries1));

I’ve attached a full example to this post. It’s not the most elegant of solutions but it does show how you can work around some of these issues by using attached properties or attached behaviours.

Hope this helps!
Andrew

4 votes

Got it working! I ended up using some different code to get the job done, which I’ll share here:

        // Custom mouse wheel usage 
        public override void OnModifierMouseWheel(ModifierMouseArgs e)
        {
            // Get region of axis
            Rect XAxisBounds = XAxis.GetBoundsRelativeTo(RootGrid);
            Rect YAxisBounds = YAxis.GetBoundsRelativeTo(RootGrid);

            // Only zoom in that axis if mouse over axis
            if      (XAxisBounds.Contains(e.MousePoint))
            {
                // If zooming in
                if      (e.Delta > 0)
                    XAxis.ZoomBy(-0.1, -0.1);
                // If zooming out
                else if (e.Delta < 0)
                    XAxis.ZoomBy( 0.1,  0.1);

                // Now don't do anything else
                e.Handled = true;
                return;
            }
            else if (YAxisBounds.Contains(e.MousePoint))
            {
                if      (e.Delta > 0)
                    YAxis.ZoomBy(-0.1, -0.1);
                else if (e.Delta < 0)
                    YAxis.ZoomBy( 0.1,  0.1);

                e.Handled = true;
                return;
            }

            // Otherwise, operate normally
            base.OnModifierMouseWheel(e);
        }

Your initial points and several key methods helped out a lot! I also simplified some parts I didn’t need. Thanks!

4 votes

Sure, you can either use a TextAnnotation for an axis label on top of the axis. Just give the position for the Y axis, 0, yaxisMax + font height. You can also use regular Labels on top of the graph if you want to do that. I think you can also make your own custom NumericAxis to be able to do this.

The second part is easier, just make two textboxes and bind them to a property called YAxisMin YAxisMax, XAxisMin, XAxisMax. You can also bind them to your axis like this.

xAxis.SetBinding(NumericAxis.MinRange, new Binding(“XAxisMin”));

good luck.

  • kewur answered 5 years ago
3 votes

Hey miles,

I’m also annoyed by this SciChartOverview width thing, so thanks for your request, it’s prompted me to look into it. Ok – so I managed to get this working by modifying the Xaml for the CreateRealTimeStockChart example like this:

        <s:SciChartSurface x:Name="priceChart">
             <!-- omitted for brevity ... -->
        </s:SciChartSurface>

        <!--  Define the Overview control, binding to XVisibleRange, which is shared between this and the SciStockChart control  -->
        <Grid Grid.Row="2" Height="32" >
          
            <!-- The grid is used to set paddings around the Overview, so that the size of the Overview matches the size of the XAxis on the parent chart -->
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20"/> <!-- Matches parent surface padding left -->
                <ColumnDefinition Width="*"/> <!-- Hosts overview control -->
                <ColumnDefinition Width="{Binding ElementName=priceChart, Path=YAxis.ActualWidth, Mode=OneWay}"/> <!-- Used to bind to parent surface YAxis -->
                <ColumnDefinition Width="15"/> <!-- Matches parent surface padding right -->
            </Grid.ColumnDefinitions>
          
            <s:SciChartOverview Grid.Column="1"
                            ParentSurface="{Binding ElementName=priceChart, Mode=OneWay}"
                            s:ThemeManager.Theme="{Binding ElementName=ThemeCombo, Path=SelectedItem}"                           
                            SelectedRange="{Binding XVisibleRange, Mode=TwoWay}" />
          
        </Grid>

The power of binding! Perhaps this ought to go into the SciChartOverview control template in future builds!

Best regards,
Andrew

3 votes
In reply to: Wrap-around ECG chart

Thanks for asking this question! This gets asked a lot so we took the time to create a short video and example for the benefit of our user-base.

Please take a look at the KB Article here on How to Create a Sweeping ECG Chart.

Best regards,
Andrew

3 votes

Hi there,

Thank you for your question! In order to synchronize two charts, you need to perform the following steps:

  1. Set the MouseManager.MouseEventGroup attached property on the root SciChartSurface.ChartModifier. This synchronizes the mouse events between two or more charts which share the same MouseEventGroup ID.

  2. Set the SciChartGroup.VerticalChartGroup attached property on the SciChartSurface itself. This synchronizes the sizes of the YAxis on multiple charts so that the charts line up

  3. Bind the XAxis.VisibleRanges together. This ensures that the charts are always in sync as you zoom / pan (eliminates rounding error).

These are demonstrated in our example Synchronize Multi Chart Mouse.

Doing this in Code Behind (no XAML)

Everything that can be done in XAML can be done in code. It is our convention to use XAML but there are various resources on the web which show you how to convert the two.

A code sample for mouse synchronization can be found below:

var scs0 = new SciChartSurface();
var scs1 = new SciChartSurface();

// omitted Axis, RenderableSeries for brevity
// ...

// 1. Set Mouse Event Group to sync mouse events
MouseManager.SetMouseEventGroup(scs0.ChartModifier, "MyGroup");
MouseManager.SetMouseEventGroup(scs1.ChartModifier, "MyGroup");

// 2. Set Vertical Chart Group to sync YAxis sizes
SciChartGroup.SetVerticalChartGroup(scs0, "ChartGroup");
SciChartGroup.SetVerticalChartGroup(scs1, "ChartGroup");

// 3. Bind XAxis together to sync XAxis.VisibleRange (avoids rounding errors)
var xAxis0 = scs0.XAxis as AxisBase;
var xAxis1 = scs1.XAxis as AxisBase;
Binding myBinding = new Binding("VisibleRange");
myBinding.Source = xAxis0.VisibleRange;
myBinding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(xAxis1, AxisBase.VisibleRangeProperty, myBinding);

Let me know if this helps!

Best regards,
Andrew

  • Andrew answered 4 years ago
  • last active 4 months ago
3 votes

Hello Vaquita,

Hmmm … you will need to set the ResamplingMode on XyScatterSeries to None. I do apologise, we should do this by default and there is an open work item to make this change (plus develop other Resampling Algorithms which are more suitable to scatter charts with no loss of visual information).

<SciChart:XyScatterRenderableSeries ResamplingMode="None">
 ...
</SciChart:XyScatterRenderableSeries>

Please try this code, it should work. The downside is it will be slower … You may have to disable AntiAliasing also to get the speed up depending on how many data-points you are adding. I also see you are using the new CrossPointMarker – is that from the v2.0 API? If so, great! If not try it vs. the old ControlTemplate point marker as it might be faster!

Best regards,
Andrew

3 votes

I have no idea. It sounds like a similar error VS puts out (in English) – the type X cannot be found in the namespace Y.

Typically when I’ve seen this its been a badly resolved reference, e.g. you have two DLLs of different versions referenced by different projects, or, you have recently upgraded the DLL version and Visual Studio has not cleared its cache.

Try restarting Visual Studio, deleting all Bin/Obj directories and doing a clean re-build. Does it work?

If not, try creating a blank project and start including these classes. Can Visual Studio see them?

Andrew

Showing 21 - 40 of 4k results