I want to write a Win8 app, there I use a Configuration-class with a static member, which is loaded once at startup and can be accessed from everywhere at runtime.

So, the main problem is, the default settings are stored within an xml file, but reading file content is asynchronous (StorageFile), but I do not find any solution to wait, until the file is completely loaded, because it is not possible to use await in every situation (main thread, constructors), what looks to me like a design issue. If the file isn't read completely before the Configuration data is accessed, there will be incorrect behaviors for this app.

here is an example code:

public class Configuration
{
    // stores the only instance of this class
    private Configuration instance = null;

    // public access to the instance
    public Configuration Current
    {
        get
        {
            if (instance == null)
            {
                instance = new Configuration();
            }

            return instance;
        }
    }

    private Configuration()
    {
        // load data from file synchronously
        // so it is loaded once on first access
    }
}

I'm not sure, how to solve this problem, probably I need to change the design of my Configuration-class. Any suggestions/hints would be nice.

有帮助吗?

解决方案

It was a design decision not to allow any synchronous calls that could take longer than 50 ms, i.e. any file or network IO calls, to make applications more responsive.

Although you can't await asynchronous calls from a constructor, nothing is stopping you from firing such calls without waiting for them to complete:

private Configuration()
{
    Init();
}

private async void Init()
{
    var contents = await FileIO.ReadTextAsync(file);
}

You can then set Configuration properties from inside Init(). If you implement INotifyPropertyChanged, you can bind the values to UI before they are loaded and the UI will refresh once they are.

If you need to check or wait for the operation to complete at some point in your app you can change the signature of Init() to return Task:

private Configuration()
{
    InitTask = Init();
}

private async Task Init()
{
    var contents = await FileIO.ReadTextAsync(file);
}

public Task InitTask { get; private set; }

Now you can check whether it is completed:

if (Configuration.Current.IsCompleted)
{
    //
}

or even await it (this will complete immediately if Task has already completed):

await Configuration.Current.InitTask;

EDIT:

If it doesn't make sense to display anything to the user until the said file is loaded, you could modify your entry page to have to alternative "views":

  • actual view (you want the user to see when the app is ready)
  • splash screen view (displaying a progress indicator for example)

You could make the right one visible based on IsCompleted property which you should expose on your viewmodel with INotifyPropertyChanged implemented.

You could then design your page as follows:

<Page xmlns:common="using:MyApp.Common">
    <Page.Resources>
        <common:BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
    </Page.Resources>
    <Grid>
        <Grid Visibility="{Binding Configuration.IsCompleted, Converter={StaticResource BoolToVisibilityConverter}">
            <!-- put your actual view here -->
        </Grid>
        <Grid Visibility="{Binding Configuration.IsNotCompleted, Converter={StaticResource BoolToVisibilityConverter}">
            <!-- put your splash screen view here -->
        </Grid>
    </Grid>
</Page>

其他提示

You don't need another class to store settings - there is AplicationData.Current.LocalSettings dictionary provided for every Windows 8 application which is loaded automatically every time the app is launched and you read it without await keyword - for example:

var settings = ApplicationData.Current.LocalSettings;
settings.Values["MyIndex"] = 2;

object value = settings.Values["MyIndex"];
if (value != null)
{
     int myIndex = (int)value;
     //...
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top