2016-07-29 55 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; 
     } 
    })); 
} 

我不喜欢这种类型的代码。但是,当我需要它时,它会工作并刷新。如果有人有更好的主意,不用客气。