Would a single resource file referenced in a window and at the app level have only one instance?

StackOverflow https://stackoverflow.com/questions/17700537

  •  03-06-2022
  •  | 
  •  

Question

I have loose form of MVVM where I have a C# class containing the resource data that my XAML is bound to. The file is referenced in two places, the MainWindow.xaml file and at the application level in the App.xaml file. When I get a referece to it in the MainWindow code-behind there seems to be only one instance and I can set values in that one instance that are seen in both the window level and the application level. Can anyone confirm or correct this assumption? see edit below

I have a MainWindow and a MainWindowResource.cs file to back the binding of the MainWindow. The resource file looks like this (shortened):

public class MainWindowResource : INotifyPropertyChanged
{
    #region Data Members

    private Color _arrowBorderColor = Colors.Black;
    private Color _arrowColor = Colors.Black;
    private bool _viewFitYAxis = true;
    private string _viewFitYAxisTooltip = "";

    #endregion Data Members

    #region Properties

    public Color ArrowBorderColor
    {
        get { return _arrowBorderColor; }
        set { _arrowBorderColor = value; SetPropertyChanged("ArrowBorderColor"); }
    }

    public Color ArrowColor
    {
        get { return _arrowColor; }
        set { _arrowColor = value; SetPropertyChanged("ArrowColor"); }
    }

    public bool ViewFitYAxis
    {
        get { return _viewFitYAxis; }
        set { _viewFitYAxis = value; SetPropertyChanged("ViewFitYAxis"); }
    }

    public string ViewFitYAxisTooltip
    {
        get { return _viewFitYAxisTooltip; }
        set { _viewFitYAxisTooltip = value; SetPropertyChanged("ViewFitYAxisTooltip"); }
    }

    #endregion Properties

    #region INotifyPropertyChanged implementation

    public event PropertyChangedEventHandler PropertyChanged;
    private void SetPropertyChanged(string prop)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }

    #endregion INotifyPropertyChanged implementation
}

I reference the MainWindowResouce file in the MainWindow XAML:

<Window.Resources>
    <c:MainWindowResource x:Key="MainWindowResource"/>

And I bind to properties of the MainWindowResource within the MainWindow XAML:

<MenuItem x:Name="MenuFitYAxis"
    Header="Fit _Y Axis"
    IsCheckable="True"
    IsChecked="{Binding Source={StaticResource MainWindowResource}, Path=ViewFitYAxis}"
    ToolTip="{Binding Source={StaticResource MainWindowResource}, Path=ViewFitYAxisTooltip}"
    Click="MenuItem_Click_ViewFitYAxis"/>

I also have a resource dictionary called ArrowResources.xaml in which I define some arrow paths. I also wished to externalize some values into the MainWindowResource file so it looks like this (shortened):

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:c="clr-namespace:UserInterface">
    <c:MainWindowResource x:Key="MainWindowResource"/>

    <Path x:Key="ArrowCounter"
        Stroke="{Binding Source={StaticResource MainWindowResource}, Path=ArrowBorderColor}"
        StrokeThickness="1"
        Data="M 8,26 L 14,20 L 10,20 A 10,10 0 1 1 30,20 L 34,20 A 14,14 0 1 0 6,20 L 2,20 Z">
        <Path.Fill>
            <RadialGradientBrush GradientOrigin="0.15,0.2" Center="0.3,0.4">
                <GradientStop Color="White"
                    Offset="0"/>
                <GradientStop Color="{Binding Source={StaticResource MainWindowResource}, Path=ArrowColor}"
                    Offset="1"/>
            </RadialGradientBrush>
        </Path.Fill>
    </Path>

</ResourceDictionary>

The resource dictionary is included in my App.xaml like this:

<Application x:Class="UserInterface.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary Source="ArrowResources.xaml"/>
    </Application.Resources>
</Application>

In the code-behind of my MainWindow I aquire a reference to the MainWindowResource class like this (shortened):

public partial class MainWindow : Window
{
    private MainWindowResource _mainWindowResource = null;
    public MainWindow()
    {
        InitializeComponent();

        // get a reference to the binding sources so we can set the properties
        _mainWindowResource = (MainWindowResource)this.Resources["MainWindowResource"];
    }
}

When I get this reference it appears that there is only one instance of the MainWindowResource class and if I set values in it, those changes will be reflected in both the MainWindow.xaml and the ArrowResources.xaml at the application level.

Can anyone confirm this, or correct my assumption?

Edit

I was wrong. The ArrowResources.xaml does see the values in MainWindowResource.cs, but when I get an instance of the class inside the MainWindow code-behind and I change the values for the arrows, the arrow paths do not recognize the changes, so it must not be the same instance.

I tried to create a separate class file for the arrows and I got an instance of that class in the code-behind of the MainWindow like this:

    private MainWindowResource _mainWindowResource = null;
    private ArrowResource _arrowResource = null;
. . .
        _mainWindowResource = (MainWindowResource)this.Resources["MainWindowResource"];
        _arrowResource = (ArrowResource)Application.Current.Resources["ArrowResource"];

But when I tried to make changes to the values in the ArrowResource class (the backing class of the resource dictionary in ArrowResources.xaml ) I could change the values but they were still not reflected in the arrow paths.

Does anyone have any idea how to make changes to these values from code-behind?

Was it helpful?

Solution

Converting my comment to a answer.

Add a public constructor to MainWindowResource class such as:

public MainWindowResource() {
  Debug.WriteLine("Called");
}

and see how many times it's called. My guess is two. A slightly tricky thing to note is just redefining a resource wouldn't result in a new object creation until that said resource is actually used in the child scope.

In your case you can just remove the resource duplicate definition from MainWindow as when it's declared in App.xaml(via the ResourceDictionary), it's already available to MainWindow.xaml making the redefinition pointless.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top