2016-07-29 3 views
0

У меня есть этот забавный случай: Страница довольно большого приложения имеет свой собственный ViewModel. Часть данных поступает из совершенно другого класса Менеджера. Мой ViewModel имеет доступ к этим данным. Каждый раз, когда класс менеджера обновляет значение, он отправляет сообщение посредника. ViewModel получает сообщение и звонит OnPropertyChanged(nameof(ManagerdataData)). У свойства ManagerData есть только получатель, чтобы получить Manager.Data. Таким образом, страница обновляет свой материал. Система работает до сих пор.Второй вид с ViewModel не обновляется

Теперь я открываю MessageBox-вещь (это System.Window), которая получает MessageBox.DataContext = this.ViewModel. Для пользовательского интерфейса он также работает. Все мои диспетчерские данные загружаются и отображаются на всех вариантах Bindings. Но: OnPropertyChanged, похоже, не имеет никакого эффекта. Страница обновляется каждый раз, когда это специальное сообщение-посредник происходит от Менеджера, а затем ViewModel возвращает OnNotificationChanged, значения привязки снова загружаются и получают новые данные от Менеджера. Но MessageBox не работает, хотя у него такой же ViewModel.

Есть ли у кого-нибудь идея о том, как это возможно?

Я думал, может быть, это еще один экземпляр (экземпляр) моей модели ViewModel. Поэтому я спрашиваю, отображается ли всплывающее окно, получить самое верхнее окно соответствующего типа и попытаться вызвать метод, содержащий эти значения OnNotificationChanged. Сначала он разбился, потому что я был в неправильной теме. Использование диспетчера приводит к зависанию всего приложения.

Любая идея? Копирование кода ... не так просто, как проект довольно большой ...

Edit: Итак, вот код:

ViewModel:

// Inside the constructor. Registers for Mediator-message. The manager sends it when a value is set. 
Mediator.Register(MediatorMessage.BackendServerCheckRefreshed,() => RefreshServerCheckUi()); 

// Method of the Mediator Message 
public void RefreshServerCheckUi() 
{ 
    OnPropertyChanged(nameof(BackendServers)); 
    OnPropertyChanged(nameof(BackendServersCommonStatus)); 
} 

// Properties that get stuff from the manager 
public BackendServerStatus BackendServersCommonStatus 
{ 
    get 
    { 
     return backendServerManager.CommonStatus; 
    } 
} 

public BackendServer[] BackendServers 
{ 
    get 
    { 
     return backendServerManager.BackendServers; 
    } 
} 

Интерфейс этого страница:

<!--Style definition--> 
<Style TargetType="Image" x:Key="BackendServerAvailability"> 
    <Style.Triggers> 
     <DataTrigger Binding="{Binding BackendServersCommonStatus}" 
        Value="{x:Static server:BackendServerStatus.NotAvailable}"> 
      <Setter Property="Source" Value="inactive.png" /> 
     </DataTrigger> 
     <DataTrigger Binding="{Binding BackendServersCommonStatus}" 
        Value="{x:Static server:BackendServerStatus.Available}"> 
      <Setter Property="Source" Value="active.png" /> 
     </DataTrigger> 
    </Style.Triggers> 
    <Style.Setters> 
     <Setter Property="Source" Value="default.png" /> 
    </Style.Setters> 
</Style> 

<!--Image--> 
<Image Style="{StaticResource BackendServerAvailability}" /> 

Эта привязка работает. Когда значение обновляется, оно отправляет сообщение посредника, это вызывает OnPropertyChanged, а затем значок получает свое изображение.

Теперь сложная часть:

// This is how I call the MessageBox from the ViewModel 
this.MessageService.ShowBackendServerCheckInfo(this); 

MessageBoxService:

// All MessageBoxes are created like this. 
public void ShowBackendServerCheckInfo(ViewModel viewModel) 
{ 
    Action action = new Action(() => 
    { 
     var args = new MessageBoxEventArgs() 
     { 
      View = new BackendServerCheckMessageBox(), 
      ViewModel = viewModel 
     }; 

     OnNewMessageBox(this, args); 
    }); 
    this.ShowMessageBox(action); 
} 

// And then called like this: 
private void ShowMessageBox(Action action) 
{ 
    /* We need to create the view in the same thread as main application. */ 
    Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, action); 
} 

Наконец в MainWindow.xaml.cs:

private async void MessageService_OnNewMessageBox(object sender, MessageBoxEventArgs e) 
{ 
    // Some Semaphore work here.. 

    var messageBox = e.View; 
    messageBox.DataContext = e.ViewModel; 
    messageBox.Owner = this; 

    messageBox.ShowDialog(); 

    // Release Semaphore 
} 

MessageBox UI:

<ItemsControlItemsSource="{Binding BackendServers}"> 
    <ItemsControl.ItemTemplate> 
     <DataTemplate> 
      <Stackpanel> 
       <TextBlock Text="{Binding Url}" /> 
       <TextBlock Text="{Binding Port}" /> 
       <Image Style="{StaticResource BackendServerAvailability}" /> 
      </Stackpanel> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 

     <!--Buttons--> 
<Stackpanel> 
    <Button Style="{StaticResource SimpleButtonStyle}" 
      Command="{Binding CloseBackendServerCheckCommand}" 
      CommandParameter="{Binding ElementName=BackendServerMsgBox}"> 
     <TextBlock Foreground="White" Margin="2" FontSize="14" 
        Text="{x:Static const:Resources.ButtonOk}" /> 
    </Button> 
    <Button Style="{StaticResource SimpleButtonStyle}" 
      Command="{Binding RestartBackendServerCheckCommand}"> 
     <TextBlock Foreground="White" Margin="2" FontSize="14" 
        Text="{x:Static const:Resources.ButtonRefresh}" /> 
    </Button> 
</StackPanel> 

Так что это был весь соответствующий код. Когда привязки не работали, у меня не было никаких значений, и кнопки не работали. Кнопка «Закрыть» отправляет свой аргумент Window в качестве аргумента, а затем объект args-Window-Object будет закрыт. Если привязки и прочее не работают, ничего не произойдет. Я исправил его до сих пор. Теперь я вижу все свои ценности. Но когда backgroundcheck отправляет сообщение о том, что один сервер недоступен, Popup-Image не обновляется во время выполнения Application-Image.

Мое предположение, что передача всего того, что EventArgs может сделать некоторые копии, и поэтому привязка к правильному экземпляру ViewModel будет потеряна ... Поэтому я сделал бы исключение для этого всплывающего окна и создавал бы его непосредственно в моей модели ViewModel. В любом случае это больше, чем любое «обычное» всплывающее окно, которое просто что-то бросает на вас, и вы нажимаете его «хорошо». В этом случае это сложнее.

Редакция 2: Aaaand, вызывающий MessageBox непосредственно из ViewModel, не изменил ничего. Это не работает. Это проблема с более чем 1 Bound View?

Edit3: Хорошо, это на самом деле работает, когда у меня есть экземпляр всплывающей и повторно установить на DataContext из ViewModel. Так что я должен найти хороший способ получить или сохранить экземпляр ...

+0

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

+0

Что может быть в окне вывода? Я думал, что это может быть больше теоретических вопросов: как вы думаете, экземпляр viewmodel просто скопирован в datacontext? – ecth

+0

* Если * вы точно представляли способ, которым моделью просмотра присвоено MessageBox.DataContext, копия кажется маловероятной. Но трудно сказать кому-то, кто говорит, по сути, «я не буду показывать вам ни один из моих кодов. Пожалуйста, восстановите все, что он делает». То, что происходит, очевидно, возможно, потому что это происходит. Мое первое дикое предположение заключалось в том, что ваша проблема заключается в том, что вы абсолютно исключены, потому что вы просто знаете, что это не может быть так. Это, наверное, не что-то экзотическое. Вероятно, это обычная ошибка, которую вы находите, следуя всему, от точки A до точки Z. –

ответ

0

Таким образом, после fiddeling вокруг меня грязный раствор:

// Only if the Popup is shown 
if (isShowingBackendServerMessageBox) 
{ 
    Application.Current.Dispatcher.BeginInvoke(new Action(() => 
    { 
     // Get the frontmost Window 
     var messageBox = Application.Current.Windows 
      ?.OfType<BackendServerCheckMessageBox>() 
      ?.FirstOrDefault(); 

     // If it is the searched MessageBox, reload all Bindings. 
     if (messageBox != null) 
     { 
      messageBox.DataContext = null; 
      messageBox.DataContext = this; 
     } 
    })); 
} 

мне не нравится этот тип кода. Но это работает и освежает, когда мне это нужно. Если у кого-то есть лучшая идея, пожалуйста.

Смежные вопросы