MahApps Not Thread Safe But Should Be [Test Case] #2870

Closed
steevcoco opened this Issue Feb 26, 2017 · 1 comment

Comments

Projects
None yet
2 participants
@steevcoco

This issue was posted before, and closed. We feel that it should be opened and resolved.

The issue is in running windows on dedicated STA Threads --- note that the issue is NOT in trivial WPF Threading, as in "invoke on the Window's Thread", but it is in resource isolation that ALLOWS multiple STA Thread Windows to run: WPF ALLOWS for a resource chain that can INTERRUPT App.xaml access.

Correctly configured, each window can have its own ThreadLocal copy of dictionaries; HOWEVER, MahApps will not run! Other configurations --- for instance, Material Design --- WILL RUN.

It takes some looking to understand things correctly [now], but is not hard, and we have a substantial app that we want to run on MahApps but cannot yet. The app will spawn windows on STA Threads (because they perform heavy refreshing on a data stream); and we have had Material Design running just fine; but wanted to port to MahApps (and we're disappointed because it is looking good).

The repo below has a simple test case. The details in making this work as we have implemented it are as follows.

What we do is define the App.xaml normally, but use a custom ResourceDictionary. This class holds a ThreadLocal cache for each Dispatcher. Windows on the main thread simply get the resources as normal from App.xaml. But windows that spawn on other threads get a ThreadLocal copy of each merged dictionary inserted into their own dictionary. This should prevent access to App.xaml from those windows but for some reason they are sharing things that access around that.

This should not be considered correct behavior.

Now looking more closely, you'll notice that we DO keep a copy of each resource IN App.xaml -- and Main Thread windows access that --- and, perhaps you'd believe that to do what we're talking about we'd need to have an empty App.xaml and fill each window with resources; however, that should not be needed based on the way Wpf architects the resource chain: we SHOULD be able to simply interrupt it. We are looking into implementing something this way instead to see if that can work.

So what we're saying here is that it seems resources IN the MahApps library should not be modeled as "Application-Wide". Whatever is accessing around this should not be doing so.

The test case is here:

https://github.com/steevcoco/MahAppsNotThreadSafe

Thanks for any consideration!

@punker76 punker76 added the Bug label Feb 26, 2017

@punker76 punker76 self-assigned this Feb 26, 2017

@punker76 punker76 added this to the 1.5.0 milestone Feb 26, 2017

@punker76 punker76 closed this in 8e0e4db Feb 26, 2017

@steevcoco

This comment has been minimized.

Show comment
Hide comment
@steevcoco

steevcoco Feb 28, 2017

This worked to a large degree; however, I still have an issue.

I have made a build with the latest code from GitHub, which has the changes to Colors.xaml. It was working until I added a shared style to an STA window. I get:

InvalidOperationException: Cannot access Freezable 'System.Windows.Media.SolidColorBrush' across threads because it cannot be frozen.

There is a GroupBox Style defined in App.xaml and it declares:

BasedOn="{StaticResource {x:Type GroupBox}}"

And it raises this exception ... And unfortunately, it does not happen in the test repo! I copied the same xaml but something is different. In my app, if I simply comment out the BasedOn then it runs fine.

This Style is defined in a shared ResourceDictionary that is included in App.xaml MergedDictionaries:

<!--
	LightAccent GroupBox Style
-->
<Style x:Key="LightAccentGroupBoxStyle"
		BasedOn="{StaticResource {x:Type GroupBox}}"
		TargetType="GroupBox">
	<Setter Property="Background">
		<Setter.Value>
			<SolidColorBrush Color="{DynamicResource AccentBaseColor}"
					Opacity=".3" />
		</Setter.Value>
	</Setter>
	<Setter Property="BorderBrush"
			Value="{DynamicResource AccentColorBrush3}" />
	<Setter Property="BorderThickness"
			Value="1" />
	<Setter Property="controls:ControlsHelper.HeaderFontWeight"
			Value="Bold" />
	<Setter Property="controls:GroupBoxHelper.HeaderForeground"
			Value="{DynamicResource ResourceKey=BlackBrush}" />
</Style>

And I use it on an STA window; and this error rises:

System.Windows.Markup.XamlParseException occurred
HResult=0x80131501
Message='Set property 'System.Windows.FrameworkElement.Style' threw an exception.' Line number '111' and line position '21'.
Source=PresentationFramework
StackTrace:
at System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri)
at System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Object rootObject, XamlAccessLevel accessLevel, Uri baseUri)
at System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream)
at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
at IMFTracker.Desktop.View.Head.HeadWindow.HeadWindow.InitializeComponent() in C:\Users\Steven\Source\Repos\IMFTracker\IMFTracker\Desktop\IMFTracker.Desktop\View\Head\HeadWindow\HeadWindow.xaml:line 1
at IMFTracker.Desktop.View.Head.HeadWindow.HeadWindow..ctor() in C:\Users\Steven\Source\Repos\IMFTracker\IMFTracker\Desktop\IMFTracker.Desktop\View\Head\HeadWindow\HeadWindow.xaml.cs:line 19

Inner Exception 1:
InvalidOperationException: Cannot access Freezable 'System.Windows.Media.SolidColorBrush' across threads because it cannot be frozen.

This worked to a large degree; however, I still have an issue.

I have made a build with the latest code from GitHub, which has the changes to Colors.xaml. It was working until I added a shared style to an STA window. I get:

InvalidOperationException: Cannot access Freezable 'System.Windows.Media.SolidColorBrush' across threads because it cannot be frozen.

There is a GroupBox Style defined in App.xaml and it declares:

BasedOn="{StaticResource {x:Type GroupBox}}"

And it raises this exception ... And unfortunately, it does not happen in the test repo! I copied the same xaml but something is different. In my app, if I simply comment out the BasedOn then it runs fine.

This Style is defined in a shared ResourceDictionary that is included in App.xaml MergedDictionaries:

<!--
	LightAccent GroupBox Style
-->
<Style x:Key="LightAccentGroupBoxStyle"
		BasedOn="{StaticResource {x:Type GroupBox}}"
		TargetType="GroupBox">
	<Setter Property="Background">
		<Setter.Value>
			<SolidColorBrush Color="{DynamicResource AccentBaseColor}"
					Opacity=".3" />
		</Setter.Value>
	</Setter>
	<Setter Property="BorderBrush"
			Value="{DynamicResource AccentColorBrush3}" />
	<Setter Property="BorderThickness"
			Value="1" />
	<Setter Property="controls:ControlsHelper.HeaderFontWeight"
			Value="Bold" />
	<Setter Property="controls:GroupBoxHelper.HeaderForeground"
			Value="{DynamicResource ResourceKey=BlackBrush}" />
</Style>

And I use it on an STA window; and this error rises:

System.Windows.Markup.XamlParseException occurred
HResult=0x80131501
Message='Set property 'System.Windows.FrameworkElement.Style' threw an exception.' Line number '111' and line position '21'.
Source=PresentationFramework
StackTrace:
at System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri)
at System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Object rootObject, XamlAccessLevel accessLevel, Uri baseUri)
at System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream)
at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
at IMFTracker.Desktop.View.Head.HeadWindow.HeadWindow.InitializeComponent() in C:\Users\Steven\Source\Repos\IMFTracker\IMFTracker\Desktop\IMFTracker.Desktop\View\Head\HeadWindow\HeadWindow.xaml:line 1
at IMFTracker.Desktop.View.Head.HeadWindow.HeadWindow..ctor() in C:\Users\Steven\Source\Repos\IMFTracker\IMFTracker\Desktop\IMFTracker.Desktop\View\Head\HeadWindow\HeadWindow.xaml.cs:line 19

Inner Exception 1:
InvalidOperationException: Cannot access Freezable 'System.Windows.Media.SolidColorBrush' across threads because it cannot be frozen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment