Click here to Skip to main content
15,878,871 members
Articles / Desktop Programming / WPF
Tip/Trick

WPF MarkupExtension to Allow Strings with Multiple Resources

Rate me:
Please Sign up or sign in to vote.
4.56/5 (8 votes)
4 Mar 2018CPOL3 min read 8.3K   58   7  
Provides a way to have a string in XAML that can contain string values and references to resources that are inserting into the string like string interpolation

Introduction

I had a case where I needed to have a string that included data from XAML resources in WPF since I would not be allowed to specify resources in the string. I immediately thought that maybe I could use the MarkupExtension for this, but was not sure exactly how. I did some searching and I was not the only one that was interested in trying to do this. I found that Martin Grech had posted some answers that allowed creating a string with a string part concatenated with a binding. His MarkupExtension had a string property and a Binding property, and this was very easy. I have included his code in the sample for reference.

With this, I immediately started working with something that was totally different since I needed to get XAML resources and I wanted to the capability to have multiple XAML resources in the string. I did consider having multiple properties but decided instead to take a leaf from something that was recently added to C#, string interpolation, and using the same format with the resource names within curly brackets. I could have gone through and manually extract the resource names from the string, but figured that RegEx would be better to solve the problem.

I did a dirty solution first using a foreach loop, but was sure that was not the best option, so shared the code on CodeProject and asked if there was a better way. -*/Sure enough there was, and Richard Deeming pointed the way.

The Code

The code is derived from MarkupExtenson and adds a single property for the string that is to be interpreted:

C#
public class ConcatExtension : MarkupExtension
 {
     private static readonly Regex Pattern = new Regex("{[^}]+}", RegexOptions.Compiled);
     public string String { get; set; }

     public override object ProvideValue(IServiceProvider serviceProvider)
     {
        DependencyObject dep = new DependencyObject();
         if (!DesignerProperties.GetIsInDesignMode(dep))
         {
             return Pattern.Replace(String, match =>
             {
                 string newMatch = match.Groups[1].Value;
                 serviceProvider.TryProvideValue(newMatch, out object value);
                 return Convert.ToString(value);
             });
         }
         return String;
     }
 }

A check was added, check for if in design mode to keep errors from occurring that would prevent the design time view from showing because of an error. A regular expression is used to find all instances where text is between two curly brackets, which have to be used to indicate where there are resource references. The RegEx Replace method with the MatchEvaluator is used to replace the resource names and associated brackets with the resource string value.

Within the MatchEvaluator code, a TryProvideValue extension method that I used with the IServiceProvider to find resources and to handle errors when a resource is not found, the method having an out argument and returning true if the resource is found. If the resource cannot be found, the out-argument details that the resource with that name could not be found and returns false:

C#
public static class ExtensionMethods
{
    public static bool TryProvideValue(this IServiceProvider serviceProvider, string key,
        out object value)
    {
        try
        {
            value = new StaticResourceExtension(key).ProvideValue(serviceProvider); ;
            return true;
        }
        catch
        {
            value = $"{{\"{key}\" could not be found}}";
            return false;
        }
    }
}

The Sample

The sample is a simple Window with two TextBlock controls whose Text DependencyPropery uses the Concat ExtensionMethod, one containing a valid resource name within curly brackets:

XML
<TextBlock HorizontalAlignment="Center"
           VerticalAlignment="Center"
           FontSize="15"
           Text="{local:Concat String='This is a test to see if the Concat MarkupExtension " +
		"really does find the resource TestString: TestString =  {TestString}'}"
           TextWrapping="Wrap" />

and the other an invalid resource name within curly brackets.

XML
<TextBlock HorizontalAlignment="Center"
           VerticalAlignment="Center"
           FontSize="15"
           Text="{local:Concat String='This is a test to see what happens when the  Concat " +
		"MarkupExtension does not find the resource TestString: TestString =  {TestString1}'}"
           TextWrapping="Wrap" />

There is only a single resource:

XML
<system:String x:Key="TestString">Testing</system:String>

Here is what the Window looks like in design mode:

Image 1

Here is what the Window looks like in run mode:

Image 2

Enhancements

There is the possibility that this could be enhanced to allow a lot more flexibility, such as allowing the use of Binding, but will have to investigate this possibility.

History

  • 03/04/2018: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Clifford Nelson Consulting
United States United States
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last eight years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with BioNano Genomics in San Diego, CA redesigning their UI for their camera system. he can be reached at qck1@hotmail.com.

Comments and Discussions

 
-- There are no messages in this forum --