You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在后台代码中嵌入并解析XAML样式至自定义控件?

Is embedding XAML as a string in code-behind and parsing it with XamlParser feasible for WPF custom controls?

Question

I have a C++ program that uses Roslyn to parse C# code. I need to convert custom controls and their corresponding XAML styles into pure code-behind. Here's an example:

XAML Style

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:CUSTOM_LIBRARY_PARSE"> 
  <Style TargetType="{x:Type local:CustomControl1}"> 
    <Setter Property="Template"> 
      <Setter.Value> 
        <ControlTemplate TargetType="{x:Type local:CustomControl1}"> 
          <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> 
            <Button Background="#FF487DF0" > 
              <Label VerticalContentAlignment="Center" HorizontalContentAlignment="Center" OpacityMask="#FFC3C3C3" Content="{Binding text_of_button_Value, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:CustomControl1}}}" /> 
            </Button> 
          </Border> 
        </ControlTemplate> 
      </Setter.Value> 
    </Setter> 
  </Style> 
</ResourceDictionary>

Control Code-Behind

using System; 
using System.Windows; 
using System.Windows.Controls; 

namespace CUSTOM_LIBRARY_PARSE { 
  public class CustomControl1 : Control { 
    public static readonly DependencyProperty text_of_button = DependencyProperty.Register( 
      "text_of_button_Value", typeof(string), typeof(CustomControl1), new PropertyMetadata(Environment.UserName) 
    ); 

    public string text_of_button_Value { 
      get { return (string)GetValue(text_of_button); } 
      set { SetValue(text_of_button, value); } 
    } 

    static CustomControl1() { 
      DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1))); 
    } 
  } 
}

I want to embed the above XAML code as a string into the code-behind, then parse this string with XamlParser and load it into CustomControl1. Is this approach feasible?


Answer

Absolutely, this approach is feasible—WPF's XamlReader (the standard API for runtime XAML parsing, which I assume you're referencing) fully supports parsing XAML from string inputs, making it straightforward to embed your style directly in code-behind. Here's how to implement it, plus key tips to avoid common pitfalls:

Step 1: Prepare the XAML string for C#

First, convert your raw XAML into a valid C# string. This requires:

  • Replacing single double quotes (") with escaped double quotes ("")
  • Using a verbatim string prefix (@) to simplify handling line breaks and avoid extra escape characters for backslashes

Your embedded XAML string will look like this:

private const string CustomControl1StyleXaml = @"
<ResourceDictionary xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" 
                    xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" 
                    xmlns:local=""clr-namespace:CUSTOM_LIBRARY_PARSE""> 
  <Style TargetType=""{x:Type local:CustomControl1}""> 
    <Setter Property=""Template""> 
      <Setter.Value> 
        <ControlTemplate TargetType=""{x:Type local:CustomControl1}""> 
          <Border Background=""{TemplateBinding Background}"" BorderBrush=""{TemplateBinding BorderBrush}"" BorderThickness=""{TemplateBinding BorderThickness}""> 
            <Button Background=""#FF487DF0"" > 
              <Label VerticalContentAlignment=""Center"" HorizontalContentAlignment=""Center"" OpacityMask=""#FFC3C3C3"" Content=""{Binding text_of_button_Value, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:CustomControl1}}}"" /> 
            </Button> 
          </Border> 
        </ControlTemplate> 
      </Setter.Value> 
    </Setter> 
  </Style> 
</ResourceDictionary>";

Step 2: Parse and load the XAML in your control

You can parse the string with XamlReader.Parse() and integrate the resulting resources into your application. For a custom control, loading the style in the static constructor ensures it's available when the control is initialized:

using System.Windows.Markup;

static CustomControl1()
{
    DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));

    try
    {
        // Parse the embedded XAML string into a ResourceDictionary
        var resourceDict = (ResourceDictionary)XamlReader.Parse(CustomControl1StyleXaml);
        // Merge the dictionary into app-level resources so it's accessible to all control instances
        Application.Current.Resources.MergedDictionaries.Add(resourceDict);
    }
    catch (XamlParseException ex)
    {
        // Handle invalid XAML gracefully (log errors, fall back to default style)
        System.Diagnostics.Debug.WriteLine($"Failed to parse control style: {ex.Message}");
    }
}

If you prefer to apply the style only to specific instances instead of globally, you can do this in the control's constructor:

public CustomControl1()
{
    var resourceDict = (ResourceDictionary)XamlReader.Parse(CustomControl1StyleXaml);
    var controlStyle = (Style)resourceDict[typeof(CustomControl1)];
    this.Style = controlStyle;
}

Key Considerations

  1. Namespace Accuracy: Double-check that the xmlns:local value in your embedded XAML exactly matches the namespace of CustomControl1—mismatches will break the style binding.
  2. XAML Feature Support: Most standard WPF XAML features (like template bindings, relative source bindings, and basic controls) work with XamlReader.Parse(), but some compile-time features (like x:Code or custom markup extensions with complex dependencies) may fail. Your example uses fully compatible features.
  3. Runtime Overhead: Parsing XAML at runtime has a small performance cost compared to compiled BAML. For a handful of controls, this is unnoticeable, but for large-scale apps, you may want to balance this against generating pure code-behind UI elements via Roslyn.
  4. Error Handling: Always wrap XamlReader.Parse() in a try-catch block to catch typos, invalid property names, or namespace errors—these will throw XamlParseException at runtime.

Alternative: Generate Pure Code-Behind UI

Since you're already using Roslyn, another option is to generate C# code that creates the ControlTemplate, Border, Button, and other elements programmatically, instead of embedding a XAML string. This eliminates runtime parsing entirely but requires more complex code generation logic to replicate the XAML's structure and bindings.


内容的提问来源于stack exchange,提问作者Samuel Hardson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:38:22