c# - 关闭启动画面窗口并打开应用程序主窗口时出现 InvalidCastException 错误
问题描述
我有一个遵循 Model View ("MainWindowView") ViewModel ("MainWindowViewModel") 模式的 C# WPF 应用程序。一切正常。没有任何类型的错误消息。
但是,当我尝试使用以下代码在应用程序开始时实现启动画面窗口(“SplashScreenWindow”)时,事情就崩溃了:https ://riptutorial.com/wpf/example/25400/creating-splash-screen-带有进度报告的窗口
启动画面窗口的代码完全可以正常工作:启动画面窗口出现并正确关闭。但是,我无法从 SplashScreenWindow 转换到 MainWindowView,收到以下“抛出异常”消息:
System.Windows.Markup.XamlParseException: ''对匹配指定绑定约束的类型'Notes.ViewModels.MainWindowViewModel' 的构造函数的调用引发了异常。' 行号“20”和行位置“10”。
内部异常 InvalidCastException:无法将“Notes.Views.SplashScreenWindow”类型的对象转换为“Notes.Views.MainWindowView”类型。
请注意,MainWindowViewModel 中的第 20 行和第 10 行位置是以下代码中的 viewmodels:MainWindowViewModel 行:
<Window.DataContext>
<viewmodels:MainWindowViewModel />
</Window.DataContext>
再次注意,如果没有启动屏幕窗口,基于 MainWindowView 和 MainWindowViewModel 的应用程序完全可以正常工作。我被卡住了,因为当我在网上搜索与错误消息的组件相关的项目时,我似乎没有找到与关闭窗口和打开 MainWindow 远程相关的任何内容。任何有关可能导致此错误的建议将不胜感激。谢谢!!
以下是各种代码片段:
应用程序.xaml
<Application x:Class="Notes.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:Notes" >
</Application>
应用程序.xaml.cs
(来源: https ://riptutorial.com/wpf/example/25400/creating-splash-screen-window-with-progress-reporting )
using Notes.Views;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Notes
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
//initialize the splash screen and set it as the application main window
var splashScreen = new SplashScreenWindow();
this.MainWindow = splashScreen;
splashScreen.Show();
//in order to ensure the UI stays responsive, we need to
//do the work on a different thread
Task.Factory.StartNew(() =>
{
//we need to do the work in batches so that we can report progress
for (int i = 1; i <= 100; i++)
{
//simulate a part of work being done
System.Threading.Thread.Sleep(30);
//because we're not on the UI thread, we need to use the Dispatcher
//associated with the splash screen to update the progress bar
splashScreen.Dispatcher.Invoke(() => splashScreen.Progress = i);
}
//once we're done we need to use the Dispatcher
//to create and show the main window
this.Dispatcher.Invoke(() =>
{
//initialize the main window, set it as the application main window
//and close the splash screen
var mainWindowView = new MainWindowView();
this.MainWindow = mainWindowView;
mainWindowView.Show();
splashScreen.Close();
});
});
}
}
}
SplashScreenWindow.xaml:
<Window x:Class="Notes.Views.SplashScreenWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodels="clr-namespace:Notes.ViewModels"
xmlns:local="clr-namespace:Notes.Views"
mc:Ignorable="d"
Title="SplashScreenWindow" Height="300" Width="500" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" WindowStyle="None" Background="DarkBlue">
<Grid>
<ProgressBar x:Name="progressBar" />
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center">Loading...</TextBlock>
</Grid>
</Window>
SplashScreenWindow.xaml.cs:
using System.Windows;
namespace Notes.Views
{
/// <summary>
/// Interaction logic for SplashScreenWindow.xaml
/// </summary>
public partial class SplashScreenWindow : Window
{
public SplashScreenWindow()
{
InitializeComponent();
}
public double Progress
{
get { return progressBar.Value; }
set { progressBar.Value = value; }
}
}
}
MainWindowView.xaml.cs
using Notes.Models;
using Notes.ViewModels;
using System;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Documents;
namespace Notes.Views
{
/// <summary>
/// Interaction logic for MainWindowView.xaml
/// </summary>
public partial class MainWindowView : Window
{
public MainWindowView()
{
InitializeComponent();
}
}
}
MainWindowView.xaml
<Window x:Class="Notes.Views.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:viewmodels="clr-namespace:Notes.ViewModels"
xmlns:models="clr-namespace:Notes.Models"
xmlns:local="clr-namespace:Notes.Views"
xmlns:usercontrols="clr-namespace:Notes.UserControls"
xmlns:converters="clr-namespace:Notes.Converters"
mc:Ignorable="d"
Background="Black"
d:DesignWidth="2736" d:DesignHeight="1500"
WindowStartupLocation="CenterScreen"
MinWidth="1550" MinHeight="1221">
<Window.DataContext>
<viewmodels:MainWindowViewModel />
</Window.DataContext>
</Window>
查看模型构造函数
public MainWindowViewModel()
{
// Assign application settings to application variables
GetSettings();
// Establish connection to database
DatabaseMethods.EstablishConnection(DatabasesFolderPath, DatabaseMainFileName);
// Open connection to database
DatabaseMethods.OpenConnection();
// Initialize SQL query statement
SQLMethods.InitializeSQLComponents();
InitializeVariables();
}
解决方案
您面临的问题是MainWindowView
在您的视图模型中引用的结果。不推荐这样做,因为这种依赖通常会带来很多问题。建议是删除这种依赖关系。
我真的不喜欢这样建议,但是如果想继续MainWindowView
在您的视图模型中引用,您应该将视图注入到视图模型中。将构造函数添加到MainWindowViewModel
接受MainWindowView
. 然后MainWindowViewModel
从 XAML 中删除实例化。现在您手动连接层次结构:
splashScreen.Close();
var mainWindow = new MainWindowView();
var viewModel = new MainWindowViewModel(mainWindow);
mainWindow.DataContext = viewModel;
mainWindow.Show();
但我建议不要这样做。不能强调这一点。您可以看到和
之间的循环依赖(两个类相互需要)。这种特殊的不需要的依赖总是会暴露出一个严重的设计问题。只需重构您的设计,这样就不需要. 这非常简单,可能只需要更改您的一般编程风格/方法。MainWindowView
MainWindowViewModel
MainWindowViewModel
MainWindowView
请用这个现代化版本替换您的代码。它需要 .NET 4.5(使用IProgress<T>
)。此版本应该可以解决您的问题:
// Declare method as async
protected override async void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Initialize the splash screen.
// The first displayed Window will be automatically assigned to Application.MainWindow
var splashScreen = new SplashScreenWindow();
splashScreen.Show();
// Because when reporting progress we won't be on the UI thread, we need to use an instance of IProgress<T>, which is
// associated with the UI thread, to update the progress bar
IProgress<double> progressReporter = new Progress<double>(value => splashScreen.Progress = value);
// In order to ensure the UI stays responsive, we need to
// execute the work asynchronously
await Task.Run(async () =>
{
for (int i = 1; i <= 100; i++)
{
// simulate a part of work being done
await Task.Delay(30);
// Post progress to the UI thread
progressReporter.Report(i);
}
});
splashScreen.Close();
// Because no Window is currently displayed,
// the first displayed Window will be automatically assigned to Application.MainWindow again
var mainWindow = new MainWindowView();
mainWindow.Show();
}
推荐阅读
- python-3.x - Python在完成后不退出函数
- javascript - JavaScript String.split 与组
- cassandra - Cassandra 数据建模以按日期时间范围检索数据
- vim - 使用来自 iex open 命令的 Elixir 和 Atom 编辑器
- javascript - 是否可以将 p5js 文件导入另一个文件?
- python-3.x - 我在 while 循环中有一个 if 函数,但它要么忽略它,要么不中断
- java - 如何在 JavaFX 中将形状放在背景中?
- java - 使用相同的变量来打印收据,因此每个项目都传递相同的值。
- reactjs - React 日期选择器问题:如何在表格中以正确的格式显示日期?
- python - 我使用 num2words 作为 pico2wave 的输入的 TTS 很差