Archive

Posts Tagged ‘.NET’

HowTo: Compress speech audio using CSpeex codec at Silverlight apps

Following up on my post on how to fix CSpeex (http://cspeex.codeplex.com) to compile at Silverlight 5 (see https://zoomicon.wordpress.com/2012/06/09/fix-compile-cspeex-audio-codec-at-silverlight-4-rc-and-silverlight-5/). 

Initially I thought it was not working correctly, but then I noticed I was running it on Terminal Server and didn’t have an audio device available. Running SLAudioDemo included in the CSpeex distribution (after doing the fix mentioned at my post above) seems to work fine on my local PC.

The SLAudioDemo demonstrates capturing audio into memory as a compressed CSpeex stream which it can decompress and save into a .WAV file that one can play say with Windows Media Player. The WAV is uncompressed, since SLAudioDemo compresses in memory (via StreamAudioSink) and decompresses to normal PCM WAV when you save (else it would be unplayable).

Obviously the Speex codec is useful for uploading (e.g. via HSS Interlink) compressed audio to a server (or to the other end at some voice chat app) and decompress there before playback (similarly the other way around).

At the server side if you want to do audio storage too, you could also keep an extra decoded copy of the compressed Speex audio data, recoded into WMA using automation of Microsoft Expression Encoder for example (see Microsoft Transform Manager, can do this in the background if you uncompress into a .WAV file at some incoming folder). That way you could serve that audio to your Silverlight client as Speex-encoded data file, but also to other users that use Windows Media Player to get the audio file from some URL etc. Could also encode to MP4/AAC or MP3 but there are licensing issues with those from what I know.

 

For the CSpeex encoder see StreamAudioSink.cs. Only 2 lines are needed in MainPage.xaml.cs of SLAudioDemo:

streamAudioSink = new StreamAudioSink();
streamAudioSink.CaptureSource = _captureSource;

after you 1st declare at the top level (must have this as a top-level field, else the garbage collector can get it)

private StreamAudioSink streamAudioSink;

 

Here’s the decoding code that saves the WAV (from MainPage.xaml.cs):

if (sfd.ShowDialog() == true)
{
  // User selected item. Only property we can get to is.
  using (Stream stream = sfd.OpenFile())
  {
    JSpeexDec decoder = new JSpeexDec();
    decoder.setDestFormat(JSpeexDec.FILE_FORMAT_WAVE);
    decoder.setStereo(true);
 
    Stream memStream = streamAudioSink.MemFile.InnerStream;
    memStream.Position = 0;
 
    decoder.decode(new RandomInputStream(memStream), 
new RandomOutputStream(stream)); stream.Close(); } }

 

Don’t get puzzled by the use of RandomInputStream / RandomOutputStream. These are from Java’s I/O package (java.io.*) that CSpeex has ported to .NET, since it’s in fact a port of JSpeex, a Java implementation of the Speex codec.

Gotcha: Silverlight’s Uri class constructor eats up part after last slash

Via trial and error, I recently found out that when creating a Uri combining another Uri and a suffix part (tried at Silverlight, but I guess it’s a .NET issue in general), it eats up the last part of the (first) Uri if it doesn’t end with "/".

That is, if you combine http://test.com/a with b.doc you get http://test.com/b.doc instead of http://test.com/a/b.doc that one might expect having used the Path.Combine of .NET on the local filesystem before (if hope I do remember well how that one behaves). So you have to make sure the first part of the path you combine has / at the end (e.g. http://test.com/a/ in the case above).

Personally I feel this is a bug at Uri class (since it breaks the least surprise principle [for developers with desktop experience at least]), but it may have been made so to follow some W3C suggestions or Javascript standard behaviour or whatever.

Example:

The following code will eat up part after last / (that is Uploads) instead of appending / (as Path would do on Windows).

const string STORAGE_URL = "http://test.com/Uploads"; //error: need "/" at end
Uri fileUri = new Uri(new Uri(STORAGE_URL), fileName + ".wav");

So, one can use instead:

const string STORAGE_URL = "http://test.com/Uploads";
Uri fileUri = new Uri(STORAGE_URL + "/" + fileName + ".wav");

Else fix the STORAGE_URL at the first sample to have "/" at the end (but this can break easily by changing the URL in the future and forgetting to add the trailing / or reading it from some configuration file and having the user enter a URL without a trailing / which can easily occur)

Fix: The tag ‘TimeUpDown’ does not exist in XML namespace (Silverlight Toolkit)

If you use TimeUpDown control from Silverlight Toolkit in your XAML like below (copy pasting from CaptionGrid at ClipFlair source):

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<DataTemplate x:Key="StartTimeCellTemplate">
<TextBlock Margin="4" Text="{Binding Begin}" />
</DataTemplate>

<DataTemplate x:Key="StartTimeCellEditTemplate">
<input:TimeUpDown Format="hh:mm:ss"
Value="{Binding Begin, Mode=TwoWay, ValidatesOnExceptions=True, NotifyOnValidationError=true}"
/>

</DataTemplate>

</ResourceDictionary>

then even though it seems to not complain in the XAML designer of Visual Studio 2010, then at Build time, even if you’ve added a reference to System.Windows.Controls.Input.Toolkit assembly (the 5.0.5.0 version for the Silverlight 5 Toolkit [December 2011 release]), you get:

The tag ‘TimeUpDown’ does not exist in XML namespace ‘clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit’

 

Similar bad behaviour occurs if you try to drag-drop the TimeUpDown control from Visual Studio toolbar (assuming you’ve installed the Silverlight Toolkit first) onto the XAML designer, no xml namespace (named xmlns:input above) is added and you just get <TimeUpDown /> in the designer.

 

The solution is to also add to your project a reference to System.Windows.Controls assembly (be sure to select same version DLL as above [e.g. 5.0.5.0]). For some reason the System.Windows.Controls.Input.Toolkit doesn’t seem to pull that together and must have some internal dependency to it (although the error you get is very cryptic).

Below is the Add Reference dialog of Visual Studio 2010, you either use the “Extensions” from the right handside, or type-in “System.Windows.Controls” at the search box to quickly spot those two assemblies.

image

 

Note that If you have a Silverlight assembly project with some UserControl in it and you apply the workaround I mentioned above, then if you use that UserControl in a Silverlight app (in a Page or other UserControl) you get a RUNTIME error this time at launch saying XamlParserError at the title bar and the exception text:

The type ‘TimeUpDown’ was not found because ‘clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit’ is an unknown namespace

This time the fix is to add a reference to "System.Windows.Controls.Input.Toolkit" to the project.

The funny thing is that this project (that uses a library that hosts a control that uses TimeUpDown itself) DOESN’T NEED to have a reference to "System.Windows.Controls", as the library does need to compile.

The other funny thing is that it does compile OK without adding the "System.Windows.Controls.Input.Toolkit" reference, but it shows runtime error.

 

This issue has been reported for some time now at:

https://connect.microsoft.com/VisualStudio/feedback/details/664106/silverlight-forum-sl5-numericupdown-control-compile-error

but hasn’t yet been fixed.

Update:

This may also be related:

https://zoomicon.wordpress.com/2012/07/02/fix-the-tag-xxx-does-not-exist-in-xml-namespace-clr-namespaceyyy/

I noticed that when I tried to compile on a machine that didn’t have Silverlight 5 Toolkit, it was eventually trying to use the Silverlight 4 Toolkit version of System.Windows.Control.Input from my Debug folder (not sure how that DLL ended up there, probably was brought in automatically via some other library’s dependencies by Visual Studio). So maybe you end up with multiple assemblies with the same name in the project and you have to remove the old ones.

You may also run into this issue if your solution was using Silverlight 5 Toolkit and you try to build the project on another machine that has Silverlight 4 Toolkit only. Solution is to install Silverlight 5 Toolkit too and then make sure the project refers to the 5.0 version of System.Windows.Controls.Input.Toolkit.dll and that the reference doesn’t have a path that points into your solution but to the program files folder instead, where Silverlight Toolkit gets installed.

Set default document for IIS via web.config at Silverlight Web project

When you tell the Visual Studio IDE to generate a web project (instead of just using an autogenerated page) for a Silverlight project, it creates a project named SilverlightAppName.Web with a SilverlightAppNameTestPage.aspx (ASP.net) and an SilverlightAppNameTestPage.html. Either one can be served from a web server (obviously the .aspx one from IIS or any web server that supports ASP.net at the server-side) to run the Silverlight app in the browser, or for debugging from within Visual Studio (with the included test web server [Cassini] or IIS).

Note that at that Web project’s Properties, there’s a tab Silverlight applications, where Silverlight apps hosted by that Web project are listed. If you add more there, Visual Studio will generate respective pages for them, in the pattern mentioned above.

By right clicking one of those two pages you can select “Set as Start Page” to set the default page that “Start” or “Start without debugging” uses. However that setting isn’t replicated to the web.config file automatically so that after publishing (by right clicking the web project and selecting “Publish”) that Web project to say a subfolder under “wwwroot” of IIS (or deeper in the web folders hierarchy).

If it was doing so, you would be able to just visit that folder and have IIS (or any other web server that supports the web.config scheme) serve the wanted page as the default document (instead of showing a can’t browse folder error or displaying the folder contents if Directory Browsing has been enabled at IIS console for that folder or some ancestor of it [and has not been overridden to disable at that folder])

However, you can edit web.config yourself to achieve that, like below:

<?xml version="1.0"?>
<!--   For more information on how to configure your ASP.NET application, 
please visit   http://go.microsoft.com/fwlink/?LinkId=169433   --> <configuration>
<system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.webServer>
<defaultDocument>
<files>
       <clear />
        <add value="
SilverlightAppNameTestPage.aspx" />
      </files>
    </defaultDocument>
  </system.webServer>

</configuration>

How to crash Visual Studio 2010 via (Silverlight) XAML recursion bug

Following up on a note about Visual Studio 2010 crashing at my previous post, this is the XAML that crashes VS2010 when opened up in a Silverlight project:

<UserControl x:Class="SilverlightApplication1.MainPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008&quot;
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
  mc:Ignorable="d"
  d:DesignHeight="300" d:DesignWidth="400">

  <UserControl.Resources>
    <ResourceDictionary>
       <Style x:Key="MyStyle" TargetType="StackPanel">
         <Setter Property="Margin" Value="0,5,0,5" />
       </Style>
    </ResourceDictionary>
  </UserControl.Resources>

  <Grid x:Name="LayoutRoot" Background="White">

    <Grid.Resources>
      <Style TargetType="StackPanel">
         <Setter Property="Style" Value="{StaticResource MyStyle}" /> <!– THIS LINE CRASHES VISUAL STUDIO (EVEN WHEN YOU TRY TO TYPE IT IN) –>
      </Style>
     </Grid.Resources>

   <StackPanel/>

  </Grid>

</UserControl>

This crashes VS2010 upon loading, because it tries to show the XAML page in the designer. I created that XAML in Notepad++ since when I was trying to type the problematic line (marked in the XAML above) it crashed VS2010 immediately before I managed to save.

BTW, you should use the XAML syntax I suggested at my previous post that uses Style inheritance instead (although it would be nice if you could do for any node <X CloneSource=…/> for any XAML node to copy its attributes and values from a same-typed node instance in some resource etc. instead of relying on the node supporting some mechanism like Style does with BasedOn [that is Style Inheritance]).

<Grid.Resources>
   <Style TargetType="StackPanel" BasedOn="{StaticResource MyStyle}" />
</Grid.Resources>

 

Speaking of crashes, VS2010 should be more clever and if it sees a certain file caused a crash (assuming it has info on file being loaded upon or just before crash), the next time of opening the solution it should warn the user against opening that file (to avoid a new crash that stops one from loading the solution).

 

Note that I tried to open up the XAML file standalone in Visual Studio and it says:

Style object is not allowed to affect the Style property of the object to which it applies.
   at System.Windows.Setter.Seal()
   at System.Windows.SetterBaseCollection.Seal()
   at System.Windows.Style.Seal()
   at System.Windows.StyleHelper.UpdateStyleCache(FrameworkElement fe, FrameworkContentElement fce, Style oldStyle, Style newStyle, Style& styleCache)
   at System.Windows.FrameworkElement.OnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
   at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
   at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
   at System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp)
   at System.Windows.FrameworkElement.UpdateStyleProperty()
   at System.Windows.TreeWalkHelper.InvalidateOnTreeChange(FrameworkElement fe, FrameworkContentElement fce, DependencyObject parent, Boolean isAddOperation)
   at System.Windows.FrameworkElement.ChangeLogicalParent(DependencyObject newParent)
   at System.Windows.FrameworkElement.AddLogicalChild(Object child)
   at System.Windows.Controls.UIElementCollection.AddInternal(UIElement element)
   at System.Windows.Controls.UIElementCollection.Add(UIElement element)
   at System.Windows.Controls.UIElementCollection.System.Collections.IList.Add(Object value)
   at Microsoft.Expression.DesignModel.InstanceBuilders.ClrObjectInstanceBuilder.InstantiateChildren(IInstanceBuilderContext context, ViewNode viewNode, DocumentCompositeNode compositeNode, Boolean isNewInstance)

Probably it loads it into an implicit WPF project. So is this some issue with Silverlight 5 XAML loading only? And if so isn’t this a potential exploit against Silverlight apps/plugin (if they allow XAML to be passed to them that is)?

It would be interesting to fix the test solution manually to not try to load the XAML file at startup, then build it and run it to see if it crashes the Silverlight plugin, the web browser, or whatever other side-effects it causes that might be exploitable.

This issue has been submitted to Microsoft at https://connect.microsoft.com/VisualStudio/feedback/details/753211/xaml-editor-window-fails-with-catastrophic-failure-when-a-style-tries-to-set-style-property. I have attached a test Silverlight project there (should be available for download soon [takes a while] from issue details)

How to space StackPanel items in XAML (has no Padding property)

While adding some properties to the back panels of ClipFlair windows, I came upon the issue of how to space items in a StackPanel. A Padding property is missing from multiple item containers (only single content controls have such), but a nice solution is described at: http://stackoverflow.com/questions/932510/how-do-i-space-out-the-child-elements-of-a-stackpanel

Sergey Aldoukhov suggested there (WPF example):

Use Margin or Padding, applied to the scope within the container:

<StackPanel>
    <StackPanel.Resources>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Margin" Value="0,10,0,0"/>
        </Style>
    </StackPanel.Resources>

    <TextBox Text="Apple"/>
    <TextBox Text="Banana"/>
    <TextBox Text="Cherry"/>
</StackPanel>

EDIT: In case you would want to re-use the margin between two containers, you can convert the margin value to a resource in an outer scope, f.e.

<Window.Resources>
    <Thickness x:Key="tbMargin">0,10,0,0</Thickness>
</Window.Resources>

and then refer to this value in the inner scope

<StackPanel.Resources>
   <Style TargetType="{x:Type TextBox}">
        <Setter Property="Margin" Value="{StaticResource tbMargin}"/>
    </Style>
</StackPanel.Resources>

 

Following up on Sergey’s suggestion there, you can define and reuse a whole Style (with various property setters, including Margin) instead of just a Thickness object:

<Style x:Key="MyStyle" TargetType="SomeItemType">
  <Setter Property="Margin" Value="0,5,0,5" />
  ...
</Style>

  <StackPanel>
    <StackPanel.Resources>
      <Style TargetType="SomeItemType" BasedOn="{StaticResource MyStyle}" />
    </StackPanel.Resources>
  ...
  </StackPanel>

Note that the trick here is the use of Style Inheritance for the implicit style, inheriting from the style in some outer (probably merged from external XAML file) resource dictionary.

Sidenote:

At first, I naively tried to use the implicit style to set the Style property of the control to that outer Style resource (say defined with the key "MyStyle"):

<StackPanel>
  <StackPanel.Resources>
    <Style TargetType="SomeItemType">
      <Setter Property="Style" Value={StaticResource MyStyle}" />
    </Style>
  </StackPanel.Resources>
</StackPanel>

which caused Visual Studio 2010 to shut down immediately with CATASTROPHIC FAILURE error (HRESULT: 0x8000FFFF (E_UNEXPECTED)), as described at https://connect.microsoft.com/VisualStudio/feedback/details/753211/xaml-editor-window-fails-with-catastrophic-failure-when-a-style-tries-to-set-style-property#

 

Note btw that there’s an even nicer solution that Elaz Katz mentioned at that StackOverflow discussion:

http://blogs.microsoft.co.il/blogs/eladkatz/archive/2011/05/29/what-is-the-easiest-way-to-set-spacing-between-items-in-stackpanel.aspx

It shows how to create an attached behavior, so that syntax like this would work:

<StackPanel local:MarginSetter.Margin="5">
   <TextBox Text="hello" />
   <Button Content="hello" />
   <Button Content="hello" />
</StackPanel>

 

Finally, another idea could be to use WPF’s UniformGrid, although that will try to space its children evenly in the available space:

http://msdn.microsoft.com/en-us/library/system.windows.controls.primitives.uniformgrid.aspx

http://www.longhorncorner.com/uploadfile/raj1979/uniformgrid-in-wpf/

Note that it has been ported to Silverlight too:

http://www.jeff.wilcox.name/2009/01/uniform-grid/

Collection of useful links for .NET, Silverlight, WPF etc. development

During the development of ClipFlair (currently at Alpha1-internal version), I’ve been doing lots of research, hunting for information (documentation, related discussion threads, useful download links) needed when writing and refactoring source code etc.

I have tried to organize these links as (Windows) Internet shortcut files into folders. They do need some further restructuring, but they can still be a useful reference. You can download the latest version of those developer links at (will add new separate releases with enhanced/refined developer links in the future):

http://clipflair.codeplex.com/releases/view/90654

Also, you can find blog posts related to issues I’ve come across up to now while developing ClipFlair at (will be updating that page):

http://clipflair.codeplex.com/documentation

C# Using Alias Directive, Namespace Alias Qualifier and a sad story

Following up on my last post:

https://zoomicon.wordpress.com/2012/07/04/how-to-compile-code-that-uses-wpf-decorator-type-in-silverlight/

I’m copying from a mental note I added to:

http://stackoverflow.com/questions/4936941/using-a-using-alias-class-with-generic-types/11334359:

As shown at:

http://msdn.microsoft.com/en-us/library/sf0df423.aspx

and

http://msdn.microsoft.com/en-us/library/c3ay4x3d%28VS.80%29.aspx

you can do:

using gen = System.Collections.Generic;

using GenList = System.Collections.Generic.List<int>;

and then use

gen::List<int> x = new gen::List<int>;

or

GenList x = new GenList();

However:

you have to replicate those using definitions at every file where you use them, so if you make some changes to them in the future and forget to update at every file, things will break badly.

I hope C# in the future will treat aliases like the do with extension methods and let you define many of them in a file that you use elsewhere, then maintain them at one place and hide the internal unnecessary type mapping details from the type consumers.

 


BTW, another issue that makes me very sad to see reoccuring. Somebody (probably a moderator) replied at that StackOverflow page above that this was an old (some year and a half ago) discussion. My reply there (I’m glad that made him remove that remark, hope I proved the point):

I do know this is old discussion, but I just had the same issue and search engine took me here – why should I care if this is old discussion? Are we doing the news?

I get even worse behaviour in Microsoft Forums (or was it Microsoft Answers?) Silverlight Forums – moderators just delete any new posts at older discussion threads (they hadn’t even bothered to lock those discussions first). The just send you a lousy e-mail about the delection of your reply without sending you the deleted content (so the time you spent posting a sometimes detailed mental note as a reply there is totally wasted if you didn’t record that reply / mental note elsewhere). You can’t even reply to their e-mail with a counter argument.

They don’t really get it that people come across these matters again and again and they get to discussions that have partial information cause nobody bothered to add some mental notes after they found the answer to the issue.

The Silverlight Forums admin reply says:

The thread to which you have attempted to reply is a very old thread, and there is no value in reviving it. As a consequence, the above post has not been approved. Replying to very old threads (aka Necro-Posting) is discouraged on the forums as it moves those old threads ahead of the sites more recent threads. We ask all members who are offerring answers to please stick to the site’s more recent threads.

If only I could reply back (you can’t):

This is nonsense, search engines don’t care of the order you show them. People go there from search engine results and find poor, non-updated replies. Think for example some issue discussed and workarrounds posted. A visitor thinks it still occurs since nobody cared to note that some newer version of Silverlight fixed it / implemented it! Or imagine that there is now a better answer to a recurring old question (that search engines keep guiding people too) due to newer developments. Just fix your forum system to not bring very old-started threads to top unless admins decide to sticks them to top. Don’t waste our time please.

Those guys haven’t even bothered to update their e-mail templates and still have references to dead URLs like:

Your post was submitted as a reply to the following thread: http://forums-legacy.silverlight.net/forums/thread/410384.aspx

Since I couldn’t reply to the post delection e-mail (reply gets rejected), I opened up the issue at their forums, so please raise your voice there too if you agree with the above concerns:

http://forums.silverlight.net/p/258209/644711.aspx/1?p=True&t=634770152705948043

(wonder why that forum post doesn’t show up there currently – some people don’t like to hear the truth when it doesn’t suit them?)

How to compile code that uses WPF Decorator type in Silverlight

At ClipFlair I’m using a modified version of FloatingWindow control, where I try to make the WPF and Silverlight flavours of the original control share as much code as possible (this is work in progress currently, hoping to eventually have the WPF and Silverlight projects both link to the same source files [this is currently true for most of those files]).

With the ControlExtensions.cs file there (which has some useful extension methods for WPF and Silverlight controls), I had an issue, since they were very similar, apart from the fact that the WPF version had 2 methods that were using Decorator type in their implementation, whereas the Silverlight ones were identical apart from using the Border  type.

WPF’s Border class extends WPF Decorator, whereas Silverlight’s Border doesn’t since Silerlight doesn’t have a Decorator class at all (to keep the size small for quick web installation they cut-off needless parts of WPF, that’s why they call Silverlight a small sibling of WPF [used to all call WPF/E if I remember well]).

Seems there’s a quick trick one can pull out to overcome this, based on C# support for “Type Aliases” with the using directive. You can add to the problematic source code file (either outside or inside any namespace declaration you have there), the following:

#if SILVERLIGHT
using Decorator = System.Windows.Controls.Border;
#endif

Then in that source code file you just use Decorator instead of Border for both WPF and Silverlight. Of course this works in method bodies, if you want to have that type in method parameters else well, you have to put the above type alias definition (called Using Alias Directive) in every source code file that uses this class. I’d prefer to put this in my WPF_Compatibility layer at ClipFlair codebase, but C# support for type aliases is very limited unfortunately, so I put a dummy WPF_Delegate.cs file there with the above usage info.

Isn’t it sad that you can place extension methods in existing namespaces, but can’t place type aliases similarly? In fact you can’t place say some of them in a file and refer to them alltogether, you have each user of your types replicate those using definitions to all code units where they use them. If you change somethine in the future in those definitions, things will break since some files will have outdated definitions (apart from having consumers of your types see unnecessary implementation details). I guess we need Alias Types, not Type Aliases (sic).

To see this in action, you can try opening in Visual Studio the FloatingWindow solution under Client folder of http://clipflair.codeplex.com source code base. You’ll notice the file Extensions/ControlExtensions.cs has a small arrow icon on it at both the Silverlight and the WPF projects, since it’s a link (added to both the projects using Add Existing File and clicking the dropdown menu next to the Add button to select “Add as Link”).

Speaking of file links in Visual Studio 2010, there seems to be a bug there. If you remove (not delete) a file from a project, then move that source file to another location (say one folder higher and into a Common subfolder), you can’t add the file to the project anymore till you Close the project (or right-click and Unload it) and Open it again (or reload it into the current Solution).

%d bloggers like this: