Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Centralize xaml/image Resources in Multi-Tier/Project Application Solution

My situation is this. I have multiple projects under a WPF solution which make up a multi-tier application I am developing. Since separate projects will sometimes need to access the same image or xaml resource I'd like to centralize "Public" resources to one project that can be referenced by any other projects requiring these "Public" resources. For example, I have a BMP image that I use in various controls and windows in separate projects that serves as a banner/logo. It seems like I should be able to add this image as a resource to the public resources project and reference it from my other projects instead of adding the image separately to every project that needs it. If this is possible, what would it look like and how should I go about doing it? If my idea just sucks I'm open to suggestions, but the project is going to be quite large so I don't want to be adding resources all over the place.

Thanks!

P.S. I have searched this topic quite a bit but there are so many garbage answers out there from people that don't know what they are doing. Given that I'm relatively new to WPF I'd rather get a direct answer to my problem.

like image 425
akagixxer Avatar asked Dec 02 '12 00:12

akagixxer


1 Answers

Well after some tinkering and research I found a solution to my own question. It seems like bits and pieces of my answer were scattered all over the web so I'll try to explain the whole thing at once, in one place. To re-cap I basically wanted to centralize any resources used across projects within my solution. These are the steps I eventually followed to accomplish this.

1. Create a User Control project in your solution (any project that can host Resource Dictionaries will do). I'll refer to this project as "the resource project".

2. Add any resource files (images etc...) that you want to share between projects to the resource project. I try to organize files in sub-directories that make sense. You will want to set the build action to "Resource" so that it gets compiled into the output binary.

3. Now add a resource dictionary to the resource project. In my case I wanted to reference several images so I made one called "ImageDictionary.xaml".

4. Add references to the image files in the resource dictionary. I am referencing images here but its just a resource dictionary, you can put whatever in there. The string in the middle is just the relative path to the image file you are referring to.

<ImageSource x:Key="SomeImageKey"> 
    Images/SomeImage.bmp
</ImageSource>

5. Now go to a project that requires the resource dictionary you just made in step 4. It can be another user control or a window, whatever. Right-click on project references and add a reference to the resource project.

6. Okay now the current project needs to reference the dictionary you made in step 4 that is defined in the resource project. I made a user control first so this is what my code would look like below... Note that I used merged dictionaries. I plan on using more later so I chose this way instead of a single dictionary. "ResourceProject" below is the name of the project/assembly that you added the resource to. "component" is a keyword that needs to be there, followed by the path to the dictionary xaml code file.

<UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/ResourceProjectAssembly;component/Resources/ImageDictionary.xaml" />  
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>        
    </UserControl.Resources>

As you can see, referencing an external resource dictionary requires something called a "Pack URI". I found a short hand way of doing it. The long way is quite a bit uglier and as far as I know, there is no advantage to it. But here is what the long way looks like.

<UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/ResourceProjectAssembly;component/Resources/ImageDictionary.xaml" />  
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>        
    </UserControl.Resources>

7. Now that your current project has a reference to the resource dictionary in the resource project, you can use anything from that dictionary. I went ahead and put an image in my user control. Below is the code. Its just like referencing a dictionary locally.

<Image Source="{StaticResource SomeImageKey}" />

8. Important Note! This caused me a headache for a while before I found it. Make sure that the startup project in your solution has a reference to the resource project EVEN IF THE STARTUP PROJECT DOES NOT USE THE RESOURCE PROJECT. (I.e. right-click references, add reference etc...) The resources will not get compiled into the output unless you do this. This part was tricky because the designer was even showing my images and I had no xaml errors but at runtime it would throw set property and cannot find file exceptions. This went away when I referenced my resource project from my startup project.

Hopefully this helps somebody. Below are some links to a few places (including stackoverflow) that gave me what I needed to put all the pieces together.

http://blogs.msdn.com/b/wpfsldesigner/archive/2010/06/03/creating-and-consuming-resource-dictionaries-in-wpf-and-silverlight.aspx

Load WPF styles or other Static Resources from an external file or assembly

ResourceDictionary in a separate assembly

like image 126
akagixxer Avatar answered Jan 04 '23 09:01

akagixxer