Search code examples
c#wpfxamlresourcedictionary

Loading resources from ResouceDictionary by key with x:Shared="True"


In WPF library I have a resource dictionary file with some templates, which need to be found by key and used as content of some control:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                xmlns:converters="clr-namespace:DbEditor.Converters"
                x:ClassModifier="internal"
                x:Class="DbEditor.Components.ScalarHandlers"
                >
<Grid x:Key="stringEditor" x:Shared="False">
    <TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"  IsEnabled="{Binding IsReadOnly}"></TextBox>
</Grid>

<Grid x:Key="intEditor" x:Shared="False">
    <TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" PreviewTextInput="IntTextBox_PreviewTextInput" IsEnabled="{Binding IsReadOnly}"></TextBox>
</Grid>
...
</ResourceDictionary>

I find one of templates by its key in this way:

var scalarDictionary = new ResourceDictionary();
scalarDictionary.Source = new Uri("/DbEditor;component/Components/Scalar.xaml", UriKind.RelativeOrAbsolute);
var pair = scalarDictionary.OfType<DictionaryEntry>().FirstOrDefault(x => (string)x.Key == key);
return pair.Value as FrameworkElement;

But added once to some control the element can't be used in another place. In past these templates were stored in App.xaml resources and had attribute x:Shared="False" which made available to use them many times. Now adding x:Shared attribute leads to runtime error

"Shared attribute in namespace 'http://schemas.microsoft.com/winfx/2006/xaml' can be used only in compiled resource dictionaries."

I have not found any way to make the resource dictionary be compiled. Changing build action on .xaml file from Page to Resources, Embedded Resources or Compile does not change any.

Is there way to load controls from ResourceDictionary at runtime by key and use them more then once?


Solution

  • The problem was solved in a kind of inelegant way. I just created a page, moved resources there and used it in place of ResourceDictionary:

    <Page x:Class="DbEditor.Components.ScalarPage" ...>
        <Page.Resources>
            <Grid x:Key="stringEditor" x:Shared="False">
                <TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"  IsEnabled="{Binding IsReadOnly}"></TextBox>
            </Grid>
    
            <Grid x:Key="intEditor" x:Shared="False">
                <TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" PreviewTextInput="IntTextBox_PreviewTextInput" IsEnabled="{Binding IsReadOnly}"></TextBox>
            </Grid>
    
        ...
    
        </Page.Resources>    
    </Page>
    

    and resouce loading:

    scalarPage = new ScalarPage();               
    return scalarPage.FindResource(key) as FrameworkElement;