Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loading config files / handling errors on startup in a WPF/MVVM app

Disclaimer: I have no prior experience in MVVM/MVC/MVP/MVWhatever, this is my very first try using any UI separation pattern.

On startup, my app needs to load data from a config file, which is needed for the application to work.

At the moment, I'm reading the config file on startup in App.xaml.cs, and I'm passing the content of the file to the view model:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        string configFile = "settings.txt";
        string[] config = File.ReadAllLines(configFile);

        var window = new MainWindow();
        var viewmodel = new MainWindowViewModel(config);
        window.DataContext = viewmodel;
        window.Show();
    }
}

1. Is this the "correct" MVVM way?
I'm sure that this way is better than reading the file directly in the view model (that's how I did it first), but I'm not sure if App.xaml.cs is the right place at all for reading config files.

2. Where/how do I handle errors?
The data from the config file is essential for the application.
So if the file is missing or empty, or if the data in the file is invalid, then I need to display an error message and quit the application.

My first attempt would be to put that in App.xaml.cs as well:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        string configFile = "settings.txt";

        if (!File.Exists(configFile))
        {
            MessageBox.Show("config file missing");
            Application.Current.Shutdown();
            return;
        }

        string[] config = File.ReadAllLines(configFile);

        if (config.Count() == 0)
        {
            MessageBox.Show("config file empty");
            Application.Current.Shutdown();
            return;
        }

        var window = new MainWindow();
        var viewmodel = new MainWindowViewModel(config);
        window.DataContext = viewmodel;
        window.Show();
    }
}

But that doesn't look "right" to me. I already didn't feel comfortable with loading the file there (see first example), so this is even worse.

Note: I know that I probably shouldn't directly call Messagebox.Show here.
Please note that this is not what I'm asking here - I know that I should replace it by something more MVVM-like, but the point is: I would still have to call that (and then close the app) from somewhere.
What I actually want to know is if App.xaml.cs is the right place for this!

3. Plus, I have another kind of error to handle:
The actual parsing of the config file is done by the model (the model is part of an existing library and the WPF app I'm building here is just a nice UI for that).
So the view model creates an instance of the model and calls the parsing method...which throws an exception if the data from the config file is invalid.

How do I handle this the "MVVM way"?
Just catching the exception in the view model and closing the whole application from there feels wrong to me.


EDIT:

To answer Will's comment about why I'm not using app.config:

I'm using a "special" configuration file because I need to load several named "sets" of value pairs from there. Here's an example file.
Basically, it's a Dictionary<string, List<ClassWithTwoStringProperties>>.

  1. AFAIK, app.config doesn't support data like this, so the only thing I could do is save the whole thing as a blob in one property in app.config, and still do the parsing myself.
    --> so I'd still have to call the parsing from somewhere in the WPF app.
  2. I wanted a simple, human editable format
  3. the WPF app is just a nice UI for a library, and I need a config file in the library as well
like image 567
Christian Specht Avatar asked Aug 19 '12 18:08

Christian Specht


1 Answers

I think, this way is incorrect.

You should think about app.config as about any persisted data (e.g. database, file). If you want to access persisted data in MVVM way, you should write a service (something like IApplicationDataService) and call this service implementation from your MainWindowViewModel code. It will be more MVVMish, if you will locate this service from some ServiceLocator or inject it via IoC container - this helps to write unit tests later.

Implementation of service should return to view model initialized model instance. Something like this:

public ApplicationDataService : IApplicationDataService
{
  public ApplicationModel LoadApplicationData()
  {
      // process app.config here
  } 
}

public ViewModelBase<TModel>
{
  public TModel Model
  {
    get { return model.Value; }
  }
  private readonly Lazy<TModel> model = new Lazy(GetModel);

  protected abstract TModel GetModel();
}

public MainWindowViewModel<ApplicationModel> : ViewModelBase
{
   protected override ApplicationModel GetModel()
   {
      try
      {
        var dataService = ServiceLocator.GetService<IApplicationDataService>();
        return dataService.LoadApplicationData();
      }
      catch (AnyException e)
      {
         // oops!
      }
   }
}
like image 102
Dennis Avatar answered Sep 28 '22 08:09

Dennis