Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make `SharedDictionaryManager` work inside XAML Designer #950

Merged
merged 18 commits into from Apr 19, 2017

Conversation

@jcansdale
Copy link
Collaborator

@jcansdale jcansdale commented Apr 4, 2017

This is same as #945 except a PR to master.

jcansdale added 7 commits Mar 30, 2017
Design time uses `file` URIs that don't work with custom `ResourceDictionary` implementations.
They need to be transformed to a `pack` URI so that the assembly that implements the custom
`ResourceDictionary` (in this case `SharedDictionaryManager`) can be found.
Any XAML that uses a custom class needs be loaded from a `pack` URI so the assembly for the custom class can be found. This makes it convert all `file` URIs, not just ones containing `SharedDictionary.xaml`.
Theses don't work when running inside the designer. This class could do with tests.
Move all caching and loading responsibilities into SharedDictionaryManagerBase.
Move theme support into ThemeDictionaryManager.
Always add ResourceDictionary to MergedDictionaries (not just when there is a cache hit).
Allow `Source` to be set multiple times to swap out ResourceDictionary (used when changing theme).
Clean up unit tests and make sure they all pass with NCrunch.
@jcansdale jcansdale requested a review from shana Apr 4, 2017
@jcansdale
Copy link
Collaborator Author

@jcansdale jcansdale commented Apr 4, 2017

@shana, This is the same as #950, except a PR with master. Let me know if you. 👍

@@ -544,6 +436,18 @@ Global
{50E277B8-8580-487A-8F8E-5C3B9FBF0F77}.XamlDesign|Any CPU.Build.0 = Release|Any CPU
{50E277B8-8580-487A-8F8E-5C3B9FBF0F77}.XamlDesign|x86.ActiveCfg = Release|Any CPU
{50E277B8-8580-487A-8F8E-5C3B9FBF0F77}.XamlDesign|x86.Build.0 = Release|Any CPU
{9453C37F-44CF-463D-B563-5C3110CEF5E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU

This comment has been minimized.

@shana

shana Apr 6, 2017
Collaborator

This project isn't actually in the solution, is there something missing?


public virtual new Uri Source
{
get { return source; }

This comment has been minimized.

@shana

shana Apr 6, 2017
Collaborator

NullGuard will throw a NRE if the getter is called when source hasn't been set yet. Mark this with [return: AllowNull] to allow a null return, or make sure source is initialized to something to avoid the NRE.

(technically it won't be called before the setter, but the XamlDesigner might decide to do something wonky, and throwing here will cause it to go boom, so probably better safe than sorry)

public class SharedDictionaryManagerBase : ResourceDictionary
{
static IDictionary<Uri, ResourceDictionary> sharedDictionaries;
static IList<IDisposable> disposables;

This comment has been minimized.

@shana

shana Apr 6, 2017
Collaborator

You can use a CompositeDisposable type here, it's designed to handle disposables in an optimal way (you can just call Dispose() on it and it will handle disposing everything it holds.

This comment has been minimized.

@shana

shana Apr 6, 2017
Collaborator

Nevermind, CompositeDisposable is an rx thing and not available here

This comment has been minimized.

@jcansdale

jcansdale Apr 6, 2017
Author Collaborator

As discussed, this is a Rx class, so will avoid taking this dependency. 😄

static IDictionary<Uri, ResourceDictionary> GetAppDomainSharedDictionaries()
{
var name = typeof(SharedDictionaryManagerBase).FullName + ".SharedDictionaries";
var sharedDictionaries = GetAppDomainData<Dictionary<Uri, ResourceDictionary>>(name);

This comment has been minimized.

@shana

shana Apr 6, 2017
Collaborator

The sharedDictionaries and the disposables list are already static across the domain, why save them in the appdomain?

This comment has been minimized.

@jcansdale

jcansdale Apr 6, 2017
Author Collaborator

The designer does some shadow assembly tricks, so the same assembly can end up being multiple times. I'm doing this to prevent stale caches from being left behind.

}

public void Dispose()
{

This comment has been minimized.

@shana

shana Apr 6, 2017
Collaborator

The pattern for Dispose should be:

bool disposed;
override void Dispose(bool disposing)
{
    if (disposed) return;
    base.Dispose(disposing); 
    disposed = true;
    if (disposing)
    {
        // dispose managed resources here
    }
}

// in base class
public void Dispose()
{
    Dispose(true);
    // GC.SuppressFinalize(this); // if there's a finalizer in this class or any subclasses
}

This comment has been minimized.

@jcansdale

jcansdale Apr 6, 2017
Author Collaborator

I tried to sidestep this by marking the class as sealed. Maybe this is a bit antisocial, but it wasn't designed with extenders in mind. It kept CodeAnalysis from complaining anyway. 😉


namespace GitHub.Helpers
{
public class SharedDictionaryManagerBase : ResourceDictionary

This comment has been minimized.

@shana

shana Apr 6, 2017
Collaborator

Given that this class needs to manage disposable resources, it should be IDisposable (if you have a CompositeDisposable instead of a List<IDisposable>, the code analysis will let you know this 😉

Copy link
Collaborator

@shana shana left a comment

Looking good! Just have a few comments around disposing resources, a possible nullref and a possible missing project

return sharedDictionaries;
}

static IList<IDisposable> GetAppDomainDisposables()

This comment has been minimized.

@shana

shana Apr 6, 2017
Collaborator

Feels like an odd name for a method that disposes of a cached list before returning it empty. Perhaps InitializeAppDomainDisposables() would be more descriptive of what this is doing (and when it should be called)?

return new Uri($"pack://application:,,,/{assemblyName};component/{path}");
}

static IDictionary<Uri, ResourceDictionary> GetAppDomainSharedDictionaries()

This comment has been minimized.

@shana

shana Apr 6, 2017
Collaborator

InitializeAppDomainSharedDictionaries or something similar is probably a better name for this method?

Add a local test SharedDictionary.xaml.
@jcansdale jcansdale force-pushed the jcansdale/xaml-designer branch from c879f2c to 8bcf6f6 Apr 6, 2017
jcansdale added 4 commits Apr 7, 2017
Everywhere now uses `GitHub.UI.ThemeDictionaryManager`.
SharedDictionaryManager can be used as base for added functionality (e.g. ThemeDictionaryManager).
@jcansdale
Copy link
Collaborator Author

@jcansdale jcansdale commented Apr 7, 2017

@shana I think it's time to stick of fork in this one. I've incorporated your suggestions, including lifecycle management using a single disposable class. Test coverage also also much better now.

Finally, I've done the grand consolidation of the 3 SharedDictionaryManagers. This touched a bunch of XAML files, but they're minor tweaks.

@shana
shana approved these changes Apr 19, 2017
@shana shana merged commit db3a26b into master Apr 19, 2017
5 checks passed
5 checks passed
GitHub CLA @jcansdale has accepted the GitHub Contributor License Agreement.
Details
VisualStudio Build #6297967 succeeded in 90s
Details
continuous-integration/appveyor/branch AppVeyor build succeeded
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
jenkins/build_log Jenkins Build Log
Details
@shana shana deleted the jcansdale/xaml-designer branch Apr 19, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

2 participants
You can’t perform that action at this time.