2016-07-29 58 views
0

我有這個有趣的案例: 一個相當大的應用程序的頁面有它自己的ViewModel。部分數據來自完全不同的經理類。我的ViewModel可以訪問這些數據。每次經理類更新一個值,它都會發送一個調解器消息。 ViewModel獲取消息並調用OnPropertyChanged(nameof(ManagerdataData))。 ManagerData財產只有一個獲得者得到Manager.Data。所以頁面會更新它的內容。該系統目前運作良好。ViewModel的第二個視圖不刷新

現在我打開一個MessageBox事物(這是一個System.Window),得到MessageBox.DataContext = this.ViewModel。對於用戶界面,它也適用。我的所有ManagerData都被加載並顯示在differend Bindings中。但是: OnPropertyChanged似乎沒有效果。 頁面每次都會刷新這個特殊的Mediator-Message來自Manager,然後ViewModel會觸發OnNotificationChanged,綁定值會重新加載並從Manager中獲取新數據。但是MessageBox不具有相同的ViewModel。

有沒有人有這樣的想法?

我想,也許這是我的ViewModel的另一個實例(副本)。所以我問是否顯示一個彈出窗口,獲取正確類型的最頂層窗口並嘗試調用包含這些OnNotificationChanged值的方法。首先它因爲我錯誤的線程而崩潰。使用調度程序會導致整個應用程序凍結。

有什麼想法?複製代碼...不容易,因爲該項目是相當大的...

編輯: 好了,所以這裏的代碼:

視圖模型:

// 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; 
    } 
} 

這樣做的UI頁面:

<!--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在應用程序圖像不會刷新時不會刷新。

我的猜測是,通過所有的東西作爲EventArgs可能會做一些副本,所以綁定到正確的ViewModel實例會丟失...所以我會爲這個彈出窗口,並直接在我的ViewModel創建它。無論如何,它比任何「平常」的彈出式菜單都要多,只是向你拋出一些東西,而你用「好吧」點擊它。在這種情況下更復雜。

編輯2: Aaaand直接從ViewModel調用MessageBox並沒有改變一件事。它不起作用。這是一個有多個綁定視圖的問題嗎?

EDIT3: 好吧,它實際上不工作,當我有彈出和重新設置的DataContext從視圖模型的實例。所以我必須找到一個很好的方式來獲得或保持實例...

+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; 
     } 
    })); 
} 

我不喜歡這種類型的代碼。但是,當我需要它時,它會工作並刷新。如果有人有更好的主意,不用客氣。