2016-07-29 13 views
0

私はこの面白いケースを持っています: 非常に大きなアプリケーションのページには独自のViewModelがあります。一部のデータは全く異なるマネージャクラスからのものです。私のViewModelはこのデータにアクセスできます。マネージャークラスは値を更新するたびに、メディエータメッセージを送信します。 ViewModelがメッセージを取得し、OnPropertyChanged(nameof(ManagerdataData))を呼び出します。 ManagerDataプロパティには、取得するゲッターがManager.Dataしかありません。そのページはその内容を更新します。これまでのところシステムが動作します。ViewModelの2番目のビューは更新されません

今私はMessageBox.DataContext = this.ViewModelを得るMessageBox物(それはSystem.Windowです)を開きます。 UIのためにも機能します。すべてのManagerDataがロードされ、differendバインディング全体に表示されます。 But: OnPropertyChangedは効果がないようです。 この特別なMediator-MessageがManagerから来て、ViewModelがOnNotificationChangedを実行するたびにページが更新され、Binding値が再ロードされ、Managerから新しいデータが取得されます。しかし、同じViewModelを持っていますが、MessageBoxはありません。

これがどのように可能であるかのアイデアはありますか?

私は、おそらくそれは私のViewModelの別のインスタンス(コピー)だと思っていました。そこで、ポップアップが表示されているかどうかを尋ね、正しいタイプの一番上のウィンドウを取得し、それらのOnNotificationChanged値を含むメソッドを呼び出そうとします。私は間違ったスレッドにあったので、最初はクラッシュしました。ディスパッチャを使用するとアプリケーション全体がフリーズします。

プロジェクトはかなり大きいですとしてコピーコードは... ...簡単ではありません

編集: わかりましたので、ここでのコード:

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

本の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:MainWindow.xaml.csで最後に

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

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 
} 

メッセージボックスの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は終了します。バインディングなどがうまくいかない場合、何も起こりません。 私はこれまでこれを修正しました。今私はすべての私の価値観を見ます。しかし、バックグラウンドチェックによって、1つのサーバが利用できないというメッセージが送信された場合、Application-Imageがポップアップイメージを更新している間、ポップアップイメージは更新されません。

EventArgsとしてすべてのものを渡すとコピーが作成され、正しいViewModelインスタンスへのバインディングが失われてしまうと思います。このポップアップのanexceptionを作成し、ViewModelで直接作成します。とにかくそれは単にあなたに何かを投げ、あなたはそれを "okay"でクリックする "いつもの"ポップアップ以上のものです。この場合はもっと複雑です。

Edit2: ViewModelからMessageBoxを直接呼び出しても、何も変更されませんでした。それは動作しません。 1バウンドビュー以上の問題はありますか?

EDIT3:私はViewModelにからポップアップし、再設定のDataContextのインスタンスを持っているとき わかりましたが、それは実際には、作業を行います。だから私は、だから、周りfiddelingた後、私は汚れたソリューションを持っている...インスタンスを取得または維持するための良い方法を見つける必要があり

+0

ビジュアルスタジオの出力ウィンドウはおそらく私が考える答えを与えるでしょう。私たちは、行動を再現するのに十分なコードなしであなたを助けることはできません。 – nkoniishvt

+0

出力ウィンドウには何がありますか?これは理論的な質問のより多くの束かもしれないと思った:あなたはviewmodelのインスタンスが単にdatacontextにコピーされていると思いますか? – ecth

+0

* ViewModelがMessageBox.DataContextに割り当てられる方法を正確に表していれば、コピーはできないようです。しかし、本質的に「私はあなたに私のコードを見せません。すべてのものを再構築してください」と言っている人には、あまり言い表せません。何が起きているのかは明らかです。私の最初の野生の推測は、あなたの問題はあなたが絶対に否定できないものにあるということでしょう。おそらく、エキゾチックなものではないでしょう。これはおそらく、ポイントAからポイントZまでのすべてを追跡することで見つけられる従来のバグです。 –

答えて

0

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

私は、コードのこのタイプが好きではありません。しかし、これは私がそれを必要とするときに動作し、リフレッシュします。誰かがより良い考えを持っているなら、あなたは大歓迎です。

関連する問題