我有这个有趣的案例:
一个相当大的应用程序的页面有自己的ViewModel。部分数据来自完全不同的Manager类。我的ViewModel可以访问这些数据。每次manager类更新一个值时,它都会发送一个Mediator消息。
ViewModel获取消息并调用OnPropertyChanged(nameof(ManagerdataData))
。 ManagerData属性只有一个获取Manager.Data
的getter。所以Page更新了它的东西。到目前为止,该系统仍在运作。
现在我打开一个获得System.Window
的MessageBox-thing(它是MessageBox.DataContext = this.ViewModel
)。对于UI它也有效。我的所有ManagerData都被加载并显示在不同的Bindings中。
但:
OnPropertyChanged似乎没有任何效果。
每次这个特殊的Mediator-Message来自Manager,然后ViewModel使用OnNotificationChanged时,Page会刷新,Binding-values会重新加载并从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;
}
}
此页面的用户界面:
<!--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}" />
此绑定有效。刷新值时,它会发送Mediator消息,调用OnPropertyChanged
,然后Icon获取其图像。
现在是棘手的部分:
// 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>
所以这就是所有相关的代码。当绑定不起作用时,我根本没有任何值,按钮也不起作用。 Close-Button发送其Window
作为参数,然后args-Window-Object将被关闭。如果Bindings和东西不起作用,什么都不会发生。
到目前为止我修好了。现在我看到了所有的价值观。但是当后台检查发送一条服务器不可用的消息时,弹出图像不会在应用程序映像执行时刷新。
我的猜测是,将所有内容传递给EventArgs可能会产生一些副本,因此绑定到正确的ViewModel实例会丢失...所以我会对此弹出窗口进行异常处理并直接在我的ViewModel中创建它。无论如何,它比任何&#34;通常&#34;只是向你抛出一些东西的弹出窗口,然后用&#34; okay&#34;点击它。在这种情况下,它会更复杂。
EDIT2: Aaaand直接从ViewModel调用MessageBox并没有改变一件事。它不起作用。这是一个有超过1个Bound View的问题吗?
EDIT3: 好吧,它确实有效,当我有弹出窗口的实例并从ViewModel重新设置DataContext时。所以我必须找到一个很好的方法来获取或保留实例......
答案 0 :(得分: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;
}
}));
}
我不喜欢这种类型的代码。但是当我需要它时,它会工作并刷新。如果有人有更好的想法,欢迎你。