tags:

views:

250

answers:

2

I have some global settings that should come from the ViewModel and should be available to all DataTemplates across all UserControls. It includes things like GlobalButtonMargin, GlobalStrokeWidth or GlobalWorkspaceBackgroundColor. Those things are in the viewmodel because the user can edit these settings at runtime.

How would you go about implementing this in a good MVVM fashion?

I thought about having a Singleton GlobalSettingsViewModel. Is this the preferred approach? If so how can I acess the singleton instance from XAML?

Another way would be to pass the GlobalSettings to all ViewModel instances that exist in my application so I can access from the viewmodels I create DataTemplates for. But that feels unclean.

A third approach would be to ditch the ViewModel approach alltogether define that as XAML resources and set the resources dynamically at runtime using FindResource.

Could you sketch out, how you would design your application to support this scenario?

+1  A: 

You could use a static you can read from and bind to, using the x:Static in your XAML. I do not like doing static global settings as it's more of an anti-pattern.

I think you should look into inversion of control/dependency injection. There are many IoC containers out there, but I usually use Unity for my dependency injection. It's available at http://prism.codeplex.com

Using IoC, you could register you settings class, and within your VM that need the data, they can easily pull out the settings you want. Your code would look something similar to this (if using unity).

var vm = container.Resolve<SomeViewModel>();

public class SomeViewModel
{
  public SomeViewModel(IUnityContainer container)
  {
      ISomeSettings settings = container.Resolve<ISomeSettings>();    
  }
}

EDIT: Or here is another solution you may be looking for:

Create your singleton:

class GlobalSettings : ViewModel
{
    private Thickness m_globalGirth;

    private static GlobalSettings m_instance = new GlobalSettings();

    public GlobalSettings()
    {
        GlobalGirth = new Thickness(2, 2, 2, 2);
    }
    public Thickness GlobalGirth
    {
        get { return m_globalGirth; }
        set
        {
            m_globalGirth = value;
            InvokePropertyChanged("GlobalGirth");
        }
    }

    public static GlobalSettings Instance
    {
        get { return m_instance; }
        set { m_instance = value; }
    }
}

Then setup your bindings:

<Window x:Class="WpfApplication3.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:WpfApplication3="clr-namespace:WpfApplication3"
    Title="Window1" Height="300" Width="300" MouseDoubleClick="Window_MouseDoubleClick">
    <Window.Resources>
        <WpfApplication3:GlobalSettings x:Key="settings" />
    </Window.Resources>
    <Grid>
        <Border BorderThickness="{Binding Source={StaticResource settings}, Path=Instance.GlobalGirth}"
                BorderBrush="Black"
                Width="100"
                Height="100" />
    </Grid>
</Window>
Jeremiah Morrill
There problem here lies somewhere else. Those settings I mentioned above are global settings that I need in almoset all of my datatemplates that I write for a lot of different viewmodels. This data is not specific to those viewmodels. They are global settings. From the WPF point of view they are more like ressources. Only that they can change at runtime. For example whenever there are lines and borders to be drawing in ANY of my datatemplates its thickness must be GlobalStrokeWidth. How can I make this available to all datatemplates. No matter what viemodel this datatemplate is for.
bitbonk
Added another solution to my original answer.
Jeremiah Morrill
Will the border pick up changes at runtimne from the INotifyPropertyChanged of GlobalSettings?
bitbonk
Absolutely! Not sure why the other guy got the points. We have the same solution, but mine has all the code.
Jeremiah Morrill
A: 

I would create the type to represent your ViewModel as a class and then define the instance of it as a resource at the ApplicationLevel. That guarentees a single instance for the entire application and you will now be able to refer to those settings using StaticResource. So for example:

<Application xmlns:myNS="clr-namespace:MyNamespace;assembly=MyAssembly" ...>
    <Application.Resources>
     <myNS:MySettings x:Key="Settings" />
    </Application.Resources>
</Application>

And then in windows/controls/templates/etc. you can access the MySettings instance using:

{Binding Source={StaticResource Settings}, Path=MyProperty}
Drew Marsh
Will the datatemplates reliably pick up changes at runtimne from the INotifyPropertyChanged of the Settings ViewModel? I always thought that StaticRessource wil only be evaluated once?
bitbonk
Yes, absolutely. You're right that the StaticResource is being evaluated once, but that's to be assigned to the Binding. However, the Binding is "live" from that point forward, so as long as you're firing property change notifications anything bound to it will be kept up to date.
Drew Marsh
How am I suppsed to access this MySettings instance from my other ViewModel objects?
bitbonk