Закрытие окна, принадлежащего другой теме - PullRequest
0 голосов
/ 20 декабря 2011

Я новичок в потоках. Я использую фоновые потоки в своем приложении WPF для связи с БД и обмена сообщениями.

Одна из моделей вида должна открывать отдельное окно. Поскольку это должно работать как поток пользовательского интерфейса, я делаю:

    private void OnSelection(SelectionType obj)
    {
        Thread thread = new Thread(ShowRegionWindow);
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
    }
    private void ShowRegionWindow()
    {
        var rWindow = new RegionWindow();
        rWindow .Show();
        rWindow .Closed += (s, e) => System.Windows.Threading.Dispatcher.ExitAllFrames();
        System.Windows.Threading.Dispatcher.Run();
    }

Теперь мне нужно закрыть это окно для другого сообщения. Как мне это сделать?

Ответы [ 2 ]

0 голосов
/ 21 декабря 2011

После прочтения некоторых ваших комментариев, я думаю, что у меня есть лучшее понимание сценария.Вот что вам нужно сделать.

Во-первых, убедитесь, что одна из ваших ViewModels имеет ссылку на модель, которая должна открывать и закрывать окно.Один из способов сделать это - внедрение зависимости от конструктора.

public ViewModel(Model model) // or IModel
{
    ...

Далее вам нужно захватить диспетчер пользовательского интерфейса в этом ViewModel.Лучшее место для этого, вероятно, также конструктор ViewModel.

private Dispatcher dispatcher;

public ViewModel(Model model)
{
    dispatcher = Dispatcher.CurrentDispatcher;
    ...

Теперь создайте два события в вашей модели;один, чтобы открыть и один, чтобы закрыть окно.

class Model
{
    internal event Action OpenWindow = delegate { };
    internal event Action CloseWindow = delegate { };
    ...

И подписаться на них в конструкторе ViewModel.

public ViewModel(Model model)
{
    dispatcher = Dispatcher.CurrentDispatcher;
    model.OpenWindow += OnWindowOpen;
    model.CloseWindow += OnWindowClose;
    ...
}

Теперь откройте и закройте окно с помощью диспетчера пользовательского интерфейса в ViewModelclass;

private Window window;

private void OnWindowOpen()
{
    // still on background thread here

    dispatcher.BeginInvoke(new ThreadStart(() =>
    {
        // now we're on the UI thread

        window = new Window();
        window.Show();
    }
}

private void OnWindowClose()
{
    dispatcher.BeginInvoke(new ThreadStart(() =>
    {
        window.Close();
    }
}

Наконец, вызовите события OpenWindow и CloseWindow из вашего фонового потока в вашей модели так же, как вы вызываете любое событие.Ваша модель может выглядеть примерно так:

class Model
{
    private Thread worker;

    internal event Action OpenWindow = delegate { };
    internal event Action CloseWindow = delegate { };

    public Model()
    {
        worker = new Thread(Work);
        worker.Start();
    }

    private void Work()
    {
        while(true)
        {
            if (/*whatever*/) OpenWindow();
            else if (/*whatever*/) CloseWindow();
        }
    }
}
0 голосов
/ 21 декабря 2011

Прежде чем я продолжу, вы сказали, что вы новичок в многопоточности, и я хочу подчеркнуть, что, вероятно, у вашего приложения нет веских причин открывать окна в разных потоках.Хорошо, что вы используете MVVM, но, возможно, вы делаете это неправильно.В идеале все ваши представления и модели представлений должны быть в основном потоке пользовательского интерфейса.Любые рабочие потоки на уровне модели должны вызывать диспетчер пользовательского интерфейса перед взаимодействием с моделью представления.Например, у вас может быть событие обновления в рабочем потоке, вызывающее обработчик в модели представления для обновления пользовательского интерфейса.Диспетчер пользовательского интерфейса должен быть вызван непосредственно до или после вызова этого события.(Чтобы было ясно, модель не должна знать о модели представления.)

Фактически, вы, похоже, создаете новое окно в обработчике событий пользовательского интерфейса, что означает, что вы, вероятно, просто должны сделать это:

private void OnSelection(SelectionType obj)
{
    var rWindow = new RegionWindow();
    rWindow.Show();
}

Однако, возможно, у вас есть вполне законная причина для того, чтобы делать это таким, какой вы есть.Если это так, один из способов закрыть это новое окно из вызывающего потока - это передать событие.Вы можете сделать что-то вроде этого:

private event Action CloseRegionWindows = delegate { }; // won't have to check for null

private void OnSelection(SelectionType obj)
{
    Thread thread = new Thread(() => ShowRegionWindow(ref CloseRegionWindows));
    ...
}

private void ShowRegionWindow(ref Action CloseRegionWindows)
{
    var rWindow = new RegionWindow();
    rWindow.Show();
    CloseRegionWindows += () => rWindow.Dispatcher.BeginInvoke(new ThreadStart(() => rWindow.Close()));
    ...
}

И затем вызвать это событие где-нибудь:

private void OnClick(object sender, RoutedEventArgs args)
{
    CloseRegionWindows();
}
...