Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF MarkupExtension to Allow Strings with Multiple Resources

0.00/5 (No votes)
4 Mar 2018 2  
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:

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:

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:

<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.

<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:

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

Here is what the Window looks like in design mode:

Here is what the Window looks like in run mode:

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here