Создать UserControl в приложении, отличном от UI. Приложение Silverlight 5 для браузера

У меня есть приложение для браузера Silverlight 5.

Существует класс

public class ActivityControl:UserControl {

    public void LoadSubControls() {
        //Creates Other UserControls, does calculations and is very slow..No refactoring..
    }
}

Мне нужно создать несколько экземпляров этого класса и вызвать метод LoadSubControls во время выполнения.

public class BasicContainer:UserControl  {

    public void CreateMultipleActivityControls() {

        for (int i = 0; i < 1000; i++) {

            ActivityControl c = new ActivityControl();  ====> I need to call this in a different thread but causes Invalid Cross Thread Exception

            c.LoadSubControls();
        }
    }
}

Есть ли способ создать несколько потоков пользовательского интерфейса, чтобы избежать недопустимого исключения перекрестного потока?

Мне нужно многопоточность по соображениям производительности и потому, что вызов метода очень медленный, и пользовательский интерфейс зависает.

Есть ли способ вызвать метод SetSyncronizationContext (который является [SecurityCritical]) в Silverlight?

Ответ 1

Нельзя создавать эти элементы управления в потоке пользовательского интерфейса, но вы можете использовать System.Threading.Tasks.Task из параллельной библиотеки задач (TPL) для обеспечения асинхронных операций.

Я смог сделать что-то подобное в silverlight 5 со структурой вроде этого. Получил оригинальную идею, глядя на источник для Caliburn.Micro.

Ниже приведено подмножество, которое относится к тому, что вы хотите.

public interface IPlatformProvider {
    /// <summary>
    ///  Executes the action on the UI thread asynchronously.
    /// </summary>
    /// <param name = "action">The action to execute.</param>
    System.Threading.Tasks.Task OnUIThreadAsync(Action action);    
}

Вот реализация.

/// <summary>
/// A <see cref="IPlatformProvider"/> implementation for the XAML platfrom (Silverlight).
/// </summary>
public class XamlPlatformProvider : IPlatformProvider {
    private Dispatcher dispatcher;

    public XamlPlatformProvider() {
       dispatcher = System.Windows.Deployment.Current.Dispatcher;
    }

    private void validateDispatcher() {
        if (dispatcher == null)
            throw new InvalidOperationException("Not initialized with dispatcher.");
    }

    /// <summary>
    ///  Executes the action on the UI thread asynchronously.
    /// </summary>
    /// <param name = "action">The action to execute.</param>
    public Task OnUIThreadAsync(System.Action action) {
        validateDispatcher();
        var taskSource = new TaskCompletionSource<object>();
        System.Action method = () => {
            try {
                action();
                taskSource.SetResult(null);
            } catch (Exception ex) {
                taskSource.SetException(ex);
            }
        };
        dispatcher.BeginInvoke(method);
        return taskSource.Task;
    }
}

Вы можете либо спуститься по пути конструктора DI, чтобы пройти в провайдере, либо использовать шаблон статического локатора, подобный этому.

/// <summary>
/// Access the current <see cref="IPlatformProvider"/>.
/// </summary>
public static class PlatformProvider {
    private static IPlatformProvider current = new XamlPlatformProvider();

    /// <summary>
    /// Gets or sets the current <see cref="IPlatformProvider"/>.
    /// </summary>
    public static IPlatformProvider Current {
        get { return current; }
        set { current = value; }
    }
}

Теперь вы сможете совершать свои звонки без блокировки основного потока и замораживания пользовательского интерфейса

public class BasicContainer : UserControl {
    public async Task CreateMultipleActivityControls() {
        var platform = PlatformProvider.Current;
        for (var i = 0; i < 1000; i++) {
            await platform.OnUIThreadAsync(() => {    
                var c = new ActivityControl();     
                c.LoadSubControls();
            });    
        }
    }
}

Если выполнение нескольких вызовов диспетчера вызвало любые проблемы с производительностью, вы могли бы переместить весь процесс на один вызов acync.

public class BasicContainer : UserControl {
    public async Task CreateMultipleActivityControls() {
        var platform = PlatformProvider.Current;
        await platform.OnUIThreadAsync(() => {
            for (var i = 0; i < 1000; i++) {                    
                var c = new ActivityControl();     
                c.LoadSubControls();
            }    
        });
    }
}