diff --git a/Help/manual/cmake-modules.7.rst b/Help/manual/cmake-modules.7.rst
index d4712baa01f4a41fcb43e7afdebf00ed3637a00d..c478a1b2e4bc1156c178e99d5a597b68a3ad4020 100644
--- a/Help/manual/cmake-modules.7.rst
+++ b/Help/manual/cmake-modules.7.rst
@@ -66,6 +66,7 @@ All Modules
    /module/CPackRPM
    /module/CPack
    /module/CPackWIX
+   /module/CSharpUtilities
    /module/CTest
    /module/CTestCoverageCollectGCOV
    /module/CTestScriptMode
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 38aba81cceaee2655ff49f547e2868976cf8982e..0e3eb86cb2ff31da0375657bb7e75cbc0e13f91c 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -366,6 +366,7 @@ Properties on Source Files
    /prop_sf/SKIP_AUTOUIC
    /prop_sf/SYMBOLIC
    /prop_sf/VS_COPY_TO_OUT_DIR
+   /prop_sf/VS_CSHARP_tagname
    /prop_sf/VS_DEPLOYMENT_CONTENT
    /prop_sf/VS_DEPLOYMENT_LOCATION
    /prop_sf/VS_INCLUDE_IN_VSIX
diff --git a/Help/module/CSharpUtilities.rst b/Help/module/CSharpUtilities.rst
new file mode 100644
index 0000000000000000000000000000000000000000..3621bbc25ea2e97f1dd71c814dbadf433956b1e6
--- /dev/null
+++ b/Help/module/CSharpUtilities.rst
@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/CSharpUtilities.cmake
diff --git a/Help/prop_sf/VS_CSHARP_tagname.rst b/Help/prop_sf/VS_CSHARP_tagname.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d42159f6daf77b68b552d8b718e7a9e70cdc653b
--- /dev/null
+++ b/Help/prop_sf/VS_CSHARP_tagname.rst
@@ -0,0 +1,19 @@
+VS_CSHARP_<tagname>
+-------------------
+
+Visual Studio and CSharp source-file-specific configuration.
+
+Tell the Visual Studio generator to set the source file tag
+``<tagname>`` to a given value in the generated Visual Studio CSharp
+project. Ignored on other generators and languages. This property
+can be used to define dependencies between source files or set any
+other Visual Studio specific parameters.
+
+Example usage:
+
+.. code-block:: cmake
+
+  set_source_files_property(<filename>
+           PROPERTIES
+           VS_CSHARP_DependentUpon <other file>
+           VS_CSHARP_SubType "Form")
diff --git a/Help/release/3.8.rst b/Help/release/3.8.rst
index d427a6377bf5eba988f5e72fbeb7ce845c04ca72..efb2aa5a75fa998a904dea1a6e29b9eb9578e03c 100644
--- a/Help/release/3.8.rst
+++ b/Help/release/3.8.rst
@@ -34,15 +34,6 @@ C#
   Visual Studio (``VS_*``) are worth a look (for setting toolset
   versions, root namespaces, assembly icons, ...).
 
-* Auto-linking in ``.csproj`` files: In C#/.NET development with
-  Visual Studio there are a number of visual editors used which
-  generate code.  Both the generated files and the ones edited
-  with the UI are connected in the ``.csproj`` file using
-  ``<DependentUpon>`` tags.  If CMake finds within a C# project
-  any source file with extension ``.Designer.cs`` or ``.xaml.cs``,
-  it checks sibling files with extension ``.xaml``, ``.settings``,
-  ``.resx`` or ``.cs`` and establishes the dependency connection.
-
 CUDA
 ^^^^
 
@@ -229,6 +220,11 @@ Properties
 Modules
 -------
 
+* A :module:`CSharpUtilities` module was added to aid parameterization of
+  Visual Studio C# targets.  It provides functions to allow automated
+  setting of source file properties to support Windows Forms, WPF/XAML or
+  other technologies as needed.
+
 * The :module:`ExternalData` module learned to support multiple
   content links for one data file using different hashes, e.g.
   ``img.png.sha256`` and ``img.png.sha1``.  This allows objects
diff --git a/Modules/CSharpUtilities.cmake b/Modules/CSharpUtilities.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..ddad85a49b1f76be20130f300861aee9b0a5b9d3
--- /dev/null
+++ b/Modules/CSharpUtilities.cmake
@@ -0,0 +1,298 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+CSharpUtilities
+---------------
+
+Functions to make configuration of CSharp/.NET targets easier.
+
+A collection of CMake utility functions useful for dealing with CSharp
+targets for Visual Studio generators from version 2010 and later.
+
+The following functions are provided by this module:
+
+**Main functions**
+
+- :command:`csharp_set_windows_forms_properties`
+- :command:`csharp_set_designer_cs_properties`
+- :command:`csharp_set_xaml_cs_properties`
+
+**Helper functions**
+
+- :command:`csharp_get_filename_keys`
+- :command:`csharp_get_filename_key_base`
+- :command:`csharp_get_dependentupon_name`
+
+Main functions provided by the module
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. command:: csharp_set_windows_forms_properties
+
+  Sets source file properties for use of Windows Forms. Use this, if your CSharp
+  target uses windows forms::
+
+    csharp_set_windows_forms_properties([<file1> [<file2> [...]]])
+
+  ``<fileN>``
+    List of all source files which are relevant for setting the
+    :prop_sf:`VS_CSHARP_<tagname>` properties (including ``.cs``, ``.resx`` and
+    ``.Designer.cs`` extensions).
+
+  In the list of all given files for all files ending with ``.Designer.cs`` and
+  ``.resx`` is searched.  For every *designer* or *resource* file a file with the
+  same base name but only ``.cs`` as extension is searched.  If this is found, the
+  :prop_sf:`VS_CSHARP_<tagname>` properties are set as follows:
+
+  for the **.cs** file:
+   - VS_CSHARP_SubType "Form"
+
+  for the **.Designer.cs** file (if it exists):
+   - VS_CSHARP_DependentUpon <cs-filename>
+   - VS_CSHARP_DesignTime "" (delete tag if previously defined)
+   - VS_CSHARP_AutoGen ""(delete tag if previously defined)
+
+  for the **.resx** file (if it exists):
+   - VS_RESOURCE_GENERATOR "" (delete tag if previously defined)
+   - VS_CSHARP_DependentUpon <cs-filename>
+   - VS_CSHARP_SubType "Designer"
+
+.. command:: csharp_set_designer_cs_properties
+
+  Sets source file properties for use of WPF/XAML. Use this, if your CSharp
+  target uses WPF/XAML::
+
+    csharp_set_designer_cs_properties([<file1> [<file2> [...]]])
+
+  ``<fileN>``
+    List of all source files which are relevant for setting the
+    :prop_sf:`VS_CSHARP_<tagname>` properties (including ``.cs``,
+    ``.resx``, ``.settings`` and ``.Designer.cs`` extensions).
+
+  In the list of all given files for all files ending with
+  ``.Designer.cs`` is searched. For every *designer* file all files
+  with the same base name but different extensions are searched. If
+  a match is found, the source file properties of the *designer* file
+  are set depending on the extension of the matched file:
+
+  if match is **.resx** file:
+   - VS_CSHARP_AutoGen "True"
+   - VS_CSHARP_DesignTime "True"
+   - VS_CSHARP_DependentUpon <resx-filename>
+
+  if match is **.cs** file:
+   - VS_CSHARP_DependentUpon <cs-filename>
+
+  if match is **.settings** file:
+   - VS_CSHARP_AutoGen "True"
+   - VS_CSHARP_DesignTimeSharedInput "True"
+   - VS_CSHARP_DependentUpon <settings-filename>
+
+.. command:: csharp_set_xaml_cs_properties
+
+  Sets source file properties for use of WPF/XAML. Use this, if your
+  CSharp target uses WPF/XAML::
+
+    csharp_set_xaml_cs_properties([<file1> [<file2> [...]]])
+
+  ``<fileN>``
+    List of all source files which are relevant for setting the
+    :prop_sf:`VS_CSHARP_<tagname>` properties (including ``.cs``,
+    ``.xaml``, and ``.xaml.cs`` extensions).
+
+  In the list of all given files for all files ending with
+  ``.xaml.cs`` is searched. For every xaml file, a file
+  with the same base name but extension ``.xaml`` is searched.
+  If a match is found, the source file properties of the ``.xaml.cs``
+  file are set:
+
+   - VS_CSHARP_DependentUpon <xaml-filename>
+
+Helper functions which are used by the above ones
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. command:: csharp_get_filename_keys
+
+  Helper function which computes a list of key values to identify
+  source files independently of relative/absolute paths given in cmake
+  and eliminates case sensitivity::
+
+    csharp_get_filename_keys(OUT [<file1> [<file2> [...]]])
+
+  ``OUT``
+    name of the variable in which the list of keys is stored
+
+  ``<fileN>``
+    filename as given to to CSharp target using :command:`add_library`
+    or :command:`add_executable`
+
+  In some way the function applies a canonicalization to the source names.
+  This is necessary to find file matches if the files have been added to
+  the target with different directory prefixes:
+
+  .. code-block:: cmake
+
+    add_library(lib
+      myfile.cs
+      ${CMAKE_CURRENT_SOURCE_DIR}/myfile.Designer.cs)
+
+    set_source_files_properties(myfile.Designer.cs PROPERTIES
+      VS_CSHARP_DependentUpon myfile.cs)
+
+    # this will fail, because in cmake
+    #  - ${CMAKE_CURRENT_SOURCE_DIR}/myfile.Designer.cs
+    #  - myfile.Designer.cs
+    # are not the same source file. The source file property is not set.
+
+.. command:: csharp_get_filename_key_base
+
+  Returns the full filepath and name **withouth** extension of a key.
+  KEY is expected to be a key from csharp_get_filename_keys. In BASE
+  the value of KEY without the file extension is returned::
+
+    csharp_get_filename_key_base(BASE KEY)
+
+  ``BASE``
+    The computed "base" of ``KEY``.
+
+  ``KEY``
+    The key of which the base will be computed. Expected to be a
+    upper case full filename.
+
+.. command:: csharp_get_dependentupon_name
+
+  Computes a string which can be used as value for the source file property
+  :prop_sf:`VS_CSHARP_<tagname>` with *target* being ``DependentUpon``::
+
+    csharp_get_dependentupon_name(NAME FILE)
+
+  ``NAME``
+    result value
+
+  ``FILE``
+    filename to convert to DependentUpon value
+
+  Actually this is only the filename without any path given at the moment.
+
+#]=======================================================================]
+
+function(csharp_get_filename_keys OUT)
+  set(${OUT} "")
+  foreach(f ${ARGN})
+    get_filename_component(f ${f} REALPATH)
+    string(TOUPPER ${f} f)
+    list(APPEND ${OUT} ${f})
+  endforeach()
+  set(${OUT} "${${OUT}}" PARENT_SCOPE)
+endfunction()
+
+function(csharp_get_filename_key_base base key)
+  get_filename_component(dir ${key} DIRECTORY)
+  get_filename_component(fil ${key} NAME_WE)
+  set(${base} "${dir}/${fil}" PARENT_SCOPE)
+endfunction()
+
+function(csharp_get_dependentupon_name out in)
+  get_filename_component(${out} ${in} NAME)
+  set(${out} ${${out}} PARENT_SCOPE)
+endfunction()
+
+function(csharp_set_windows_forms_properties)
+  csharp_get_filename_keys(fileKeys ${ARGN})
+  foreach(key ${fileKeys})
+    get_filename_component(ext ${key} EXT)
+    if(${ext} STREQUAL ".DESIGNER.CS" OR
+       ${ext} STREQUAL ".RESX")
+      csharp_get_filename_key_base(NAME_BASE ${key})
+      list(FIND fileKeys "${NAME_BASE}.CS" FILE_INDEX)
+      if(NOT ${FILE_INDEX} EQUAL -1)
+        list(GET ARGN ${FILE_INDEX} FILE_NAME)
+        # set properties of main form file
+        set_source_files_properties("${FILE_NAME}"
+          PROPERTIES
+          VS_CSHARP_SubType "Form")
+        csharp_get_dependentupon_name(LINK "${FILE_NAME}")
+        # set properties of designer file (if found)
+        list(FIND fileKeys "${NAME_BASE}.DESIGNER.CS" FILE_INDEX)
+        if(NOT ${FILE_INDEX} EQUAL -1)
+          list(GET ARGN ${FILE_INDEX} FILE_NAME)
+          set_source_files_properties("${FILE_NAME}"
+            PROPERTIES
+            VS_CSHARP_DependentUpon "${LINK}"
+            VS_CSHARP_DesignTime ""
+            VS_CSHARP_AutoGen "")
+        endif()
+        # set properties of corresponding resource file (if found)
+        list(FIND fileKeys "${NAME_BASE}.RESX" FILE_INDEX)
+        if(NOT ${FILE_INDEX} EQUAL -1)
+          list(GET ARGN ${FILE_INDEX} FILE_NAME)
+          set_source_files_properties("${FILE_NAME}"
+            PROPERTIES
+            VS_RESOURCE_GENERATOR ""
+            VS_CSHARP_DependentUpon "${LINK}"
+            VS_CSHARP_SubType "Designer")
+        endif()
+      endif()
+    endif()
+  endforeach()
+endfunction()
+
+function(csharp_set_designer_cs_properties)
+  csharp_get_filename_keys(fileKeys ${ARGN})
+  set(INDEX -1)
+  foreach(key ${fileKeys})
+    math(EXPR INDEX "${INDEX}+1")
+    list(GET ARGN ${INDEX} source)
+    get_filename_component(ext ${key} EXT)
+    if(${ext} STREQUAL ".DESIGNER.CS")
+      csharp_get_filename_key_base(NAME_BASE ${key})
+      if("${NAME_BASE}.RESX" IN_LIST fileKeys)
+        list(FIND fileKeys "${NAME_BASE}.RESX" FILE_INDEX)
+        list(GET ARGN ${FILE_INDEX} FILE_NAME)
+        csharp_get_dependentupon_name(LINK "${FILE_NAME}")
+        set_source_files_properties("${source}"
+          PROPERTIES
+          VS_CSHARP_AutoGen "True"
+          VS_CSHARP_DesignTime "True"
+          VS_CSHARP_DependentUpon "${LINK}")
+      elseif("${NAME_BASE}.CS" IN_LIST fileKeys)
+        list(FIND fileKeys "${NAME_BASE}.CS" FILE_INDEX)
+        list(GET ARGN ${FILE_INDEX} FILE_NAME)
+        csharp_get_dependentupon_name(LINK "${FILE_NAME}")
+        set_source_files_properties("${source}"
+          PROPERTIES
+          VS_CSHARP_DependentUpon "${LINK}")
+      elseif("${NAME_BASE}.SETTINGS" IN_LIST fileKeys)
+        list(FIND fileKeys "${NAME_BASE}.SETTINGS" FILE_INDEX)
+        list(GET ARGN ${FILE_INDEX} FILE_NAME)
+        csharp_get_dependentupon_name(LINK "${FILE_NAME}")
+        set_source_files_properties("${source}"
+          PROPERTIES
+          VS_CSHARP_AutoGen "True"
+          VS_CSHARP_DesignTimeSharedInput "True"
+          VS_CSHARP_DependentUpon "${LINK}")
+      endif()
+    endif()
+  endforeach()
+endfunction()
+
+function(csharp_set_xaml_cs_properties)
+  csharp_get_filename_keys(fileKeys ${ARGN})
+  set(INDEX -1)
+  foreach(key ${fileKeys})
+    math(EXPR INDEX "${INDEX}+1")
+    list(GET ARGN ${INDEX} source)
+    get_filename_component(ext ${key} EXT)
+    if(${ext} STREQUAL ".XAML.CS")
+      csharp_get_filename_key_base(NAME_BASE ${key})
+      if("${NAME_BASE}.XAML" IN_LIST fileKeys)
+        list(FIND fileKeys "${NAME_BASE}.XAML" FILE_INDEX)
+        list(GET ARGN ${FILE_INDEX} FILE_NAME)
+        csharp_get_dependentupon_name(LINK "${FILE_NAME}")
+        set_source_files_properties("${source}"
+          PROPERTIES
+          VS_CSHARP_DependentUpon "${LINK}")
+      endif()
+    endif()
+  endforeach()
+endfunction()
diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h
index b193f65e17a1277d3ab3ccf3565094d883d9933d..bbcc3006b6ba164256573f05f356a603a9d261d0 100644
--- a/Source/cmSourceFile.h
+++ b/Source/cmSourceFile.h
@@ -86,6 +86,7 @@ public:
 
   // Get the properties
   cmPropertyMap& GetProperties() { return this->Properties; }
+  const cmPropertyMap& GetProperties() const { return this->Properties; }
 
   /**
    * Check whether the given source file location could refer to this
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 21bb77453977359c447f9f9ddba84c1dbee03ba2..7447821a3d70cb1ceff402c9c304ae8c0eb9269e 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -681,20 +681,40 @@ void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup()
           if (const char* g = (*oi)->GetProperty("VS_RESOURCE_GENERATOR")) {
             generator = g;
           }
-          this->WriteString("<Generator>", 3);
-          (*this->BuildFileStream) << cmVS10EscapeXML(generator)
-                                   << "</Generator>\n";
-          if (designerResource.find(srcDir) == 0) {
-            designerResource = designerResource.substr(srcDir.length() + 1);
-          } else if (designerResource.find(binDir) == 0) {
-            designerResource = designerResource.substr(binDir.length() + 1);
-          } else {
-            designerResource =
-              cmsys::SystemTools::GetFilenameName(designerResource);
+          if (!generator.empty()) {
+            this->WriteString("<Generator>", 3);
+            (*this->BuildFileStream) << cmVS10EscapeXML(generator)
+                                     << "</Generator>\n";
+            if (designerResource.find(srcDir) == 0) {
+              designerResource = designerResource.substr(srcDir.length() + 1);
+            } else if (designerResource.find(binDir) == 0) {
+              designerResource = designerResource.substr(binDir.length() + 1);
+            } else {
+              designerResource =
+                cmsys::SystemTools::GetFilenameName(designerResource);
+            }
+            this->ConvertToWindowsSlash(designerResource);
+            this->WriteString("<LastGenOutput>", 3);
+            (*this->BuildFileStream) << designerResource
+                                     << "</LastGenOutput>\n";
+          }
+        }
+        const cmPropertyMap& props = (*oi)->GetProperties();
+        for (cmPropertyMap::const_iterator p = props.begin(); p != props.end();
+             ++p) {
+          static const std::string propNamePrefix = "VS_CSHARP_";
+          if (p->first.find(propNamePrefix.c_str()) == 0) {
+            std::string tagName = p->first.substr(propNamePrefix.length());
+            if (!tagName.empty()) {
+              std::string value = props.GetPropertyValue(p->first);
+              if (!value.empty()) {
+                this->WriteString("<", 3);
+                (*this->BuildFileStream) << tagName << ">";
+                (*this->BuildFileStream) << cmVS10EscapeXML(value);
+                (*this->BuildFileStream) << "</" << tagName << ">\n";
+              }
+            }
           }
-          this->ConvertToWindowsSlash(designerResource);
-          this->WriteString("<LastGenOutput>", 3);
-          (*this->BuildFileStream) << designerResource << "</LastGenOutput>\n";
         }
       }
 
@@ -1941,42 +1961,21 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
         sourceFileTags["Link"] = link;
       }
     }
-    // check if file is a generated .Designer.cs or .xaml.cs file
-    // to add additional necessary tags
-    const std::string fileExtension =
-      cmsys::SystemTools::GetFilenameExtension(f);
-    if (fileExtension == ".Designer.cs" || fileExtension == ".xaml.cs") {
-      f = f.substr(0, f.length() - fileExtension.length());
-      if (sourceFileTags.find("Link") == sourceFileTags.end() &&
-          !this->InSourceBuild) {
-        // add link fallback
-        sourceFileTags["Link"] =
-          cmsys::SystemTools::GetFilenameName(f) + fileExtension;
-      }
-      std::vector<std::string> extensions;
-      extensions.push_back(".resx");
-      extensions.push_back(".settings");
-      extensions.push_back(".xaml");
-      extensions.push_back(".cs");
-      std::string dependencyExtension;
-      for (std::vector<std::string>::iterator i = extensions.begin();
-           i != extensions.end(); ++i) {
-        if (cmsys::SystemTools::FileExists(f + *i)) {
-          dependencyExtension = *i;
-          // There should never be more than one match. Otherwise
-          // one cannot tell on which match the file depends.
-          break;
+    const cmPropertyMap& props = sf.GetProperties();
+    for (cmPropertyMap::const_iterator p = props.begin(); p != props.end();
+         ++p) {
+      static const std::string propNamePrefix = "VS_CSHARP_";
+      if (p->first.find(propNamePrefix.c_str()) == 0) {
+        std::string tagName = p->first.substr(propNamePrefix.length());
+        if (!tagName.empty()) {
+          const std::string val = props.GetPropertyValue(p->first);
+          if (!val.empty()) {
+            sourceFileTags[tagName] = val;
+          } else {
+            sourceFileTags.erase(tagName);
+          }
         }
       }
-      if (dependencyExtension == ".resx") {
-        sourceFileTags["DesignTime"] = "True";
-        sourceFileTags["AutoGen"] = "True";
-      } else if (dependencyExtension == ".settings") {
-        sourceFileTags["DesignTimeSharedInput"] = "True";
-        sourceFileTags["AutoGen"] = "True";
-      }
-      sourceFileTags["DependentUpon"] =
-        cmsys::SystemTools::GetFilenameName(f) + dependencyExtension;
     }
     // write source file specific tags
     if (!sourceFileTags.empty()) {
diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
index bc1ec9779b24687ab4b43d96f6ea5c34d929f55c..3af877f4ff148c91a3bfba70aedb44cd9d5e8ae6 100644
--- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
@@ -3,3 +3,4 @@ run_cmake(VsConfigurationType)
 run_cmake(VsTargetsFileReferences)
 run_cmake(VsCustomProps)
 run_cmake(VsDebuggerWorkingDir)
+run_cmake(VsCSharpCustomTags)
diff --git a/Tests/RunCMake/VS10Project/VsCSharpCustomTags-check.cmake b/Tests/RunCMake/VS10Project/VsCSharpCustomTags-check.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..70ea193268a0ac5093c7b3419ef497bce8af4e70
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsCSharpCustomTags-check.cmake
@@ -0,0 +1,23 @@
+set(csProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.csproj")
+if(NOT EXISTS "${csProjectFile}")
+  set(RunCMake_TEST_FAILED "Project file ${csProjectFile} does not exist.")
+  return()
+endif()
+
+set(tagFound FALSE)
+
+set(tagName "MyCustomTag")
+set(tagValue "MyCustomValue")
+
+file(STRINGS "${csProjectFile}" lines)
+foreach(line IN LISTS lines)
+  if(line MATCHES "^ *<${tagName}>${tagValue}</${tagName}>")
+    message(STATUS "foo.csproj has tag ${tagName} with value ${tagValue} defined")
+    set(tagFound TRUE)
+  endif()
+endforeach()
+
+if(NOT tagFound)
+  set(RunCMake_TEST_FAILED "Source file tag ${tagName} with value ${tagValue} not found.")
+  return()
+endif()
diff --git a/Tests/RunCMake/VS10Project/VsCSharpCustomTags.cmake b/Tests/RunCMake/VS10Project/VsCSharpCustomTags.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..c51e9c370ef8567507456de70e76e43b8d66d9eb
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsCSharpCustomTags.cmake
@@ -0,0 +1,11 @@
+enable_language(CSharp)
+add_library(foo foo.cs)
+
+set(props_file "${CMAKE_CURRENT_SOURCE_DIR}/my.props")
+
+set(tagName "MyCustomTag")
+set(tagValue "MyCustomValue")
+
+set_source_files_properties(foo.cs
+  PROPERTIES
+  VS_CSHARP_${tagName} "${tagValue}")
diff --git a/Tests/RunCMake/VS10Project/foo.cs b/Tests/RunCMake/VS10Project/foo.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3695dc91e30e8d1c758400c9be1c00ba0a6fab60
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/foo.cs
@@ -0,0 +1,3 @@
+void foo()
+{
+}