我对WPF和MVVM概念很陌生,我试图解决可能愚蠢的问题。我有MainViewModel,我控制什么应该在屏幕上看到根据按下的按钮在菜单。
class MainViewModel : ObservableObject
{
public RelayCommand HomeViewCommand { get; set; }
public RelayCommand SettingsViewCommand { get; set; }
public HomeViewModel HomeVM { get; set; }
public SettingsViewModel SettingsVM { get; set; }
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set
{
_currentView = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
HomeVM = new HomeViewModel();
SettingsVM = new SettingsViewModel();
CurrentView = HomeVM;
HomeViewCommand = new RelayCommand(o =>
{
CurrentView = HomeVM;
});
SettingsViewCommand = new RelayCommand(o =>
{
CurrentView = SettingsVM;
});
}
我的一个视图是SettingsView。在这个视图中,我有一个按钮,它应该检查连接字符串是否正确。因为我要用到很多SQL命令我的想法是,把所有关于SQL的代码放到一个文件夹里。所以基本上项目是MVVM(file)>View(file),ViewModel(file)....和SQL(文件)在…可悲的是,当我添加到setingsview sql时,应用程序因为'对象引用未设置为对象的实例'而下降
在setingsview中是:
public partial class SettingsView : UserControl
{
private readonly HSQLTestConnection _SQLTestConnection;
public SettingsView(HSQLTestConnection SQLTestConnection)
{
InitializeComponent();
_SQLTestConnection = SQLTestConnection;
按钮是这样的:_SQLTestConnection.TryConnectionString(ConnectionString);
模型为空:
class SettingsViewModel
{
public SettingsViewModel()
{
}
}
SQL的接口是:
public interface HSQLTestConnection
{
void TryConnectionString(string ConnectionString);
}
和SQL函数:
public class SQLTestConnection : HSQLTestConnection
{
public void TryConnectionString(string ConnectionString)
{
//do something
}
}
应用程序不显示任何错误,正在工作和改变视图很好,我认为问题是与公共SettingsView(HSQLTestConnection SQLTestConnection)但我找不到解决这个问题的方法。因为我将有多个SQL类,所以我想用这种方式解决它。它曾经在我编写的不同应用程序中工作,但我没有使用RellayCommand,我想尝试一下。提前感谢您的帮助。
我看到SettingsView
类扩展了UserControl
类。这有问题,因为
- 你说你想遵循MVVM设计模式,但你的用户控制代码(
SettingsView
)在构造函数中引用了你的数据库连接(HSQLTestConnection
)。你不应该混合视图代码与数据库代码。 - 你的
SettingsView
是由框架初始化的,所以它应该在构造函数中获得HSQLTestConnection
参数的值?
在任何情况下,你的视图不应该有任何数据库逻辑,至少如果你想遵循MVVM。
您可以做的是在设置视图SettingsViewModel
的视图模型中启动数据库连接。不是很好,但稍微好一点。
既然你提到会有"相当多的SQL命令",我建议你把这些代码分离成一个专用的服务,然后在你的视图模型中使用这个服务。
public interface IMyDatabaseService
{
bool TestConnection();
IEnumerable<SomeDataObject> GetData();
...
}
然后在需要的地方传递这个接口:
public class SettingsViewModel : INotifyPropertyChanged
{
private readonly IMyDatabaseService databaseService;
private bool connectionSuccessful;
public RelayCommand TestConnectionCommand { get; set; }
public bool ConnectionSuccessful
{
get => connectionSuccessful;
set
{
connectionSuccessfull = value;
var propertyName = nameof(ConnectionSuccessful);
PropertyChanged?.Invoke(this, new (propertyName));
}
}
public SettingsViewModel(IMyDatabaseService databaseService)
{
this.databaseService = databaseService;
TestConnectionCommand = new RelayCommand(_ =>
ConnectionSuccessful = databaseService.TestConnection()
);
...
}
}
public class MainViewModel
{
...
public MainViewModel()
{
...
IMyDatabaseService databaseService = new MyDatabaseService();
SettingsVM = new SettingsViewModel(databaseService);
...
}
...
}
注意TestConnectionCommand
和ConnectionSuccesful
布尔值。
该命令将调用数据库服务,这是您将绑定按钮的对象。
类似地,可以在视图中绑定布尔值以显示连接状态。由于无论何时运行该命令,该值都可能发生变化,因此必须调用PropertyChanged
来让框架知道该值发生了变化。在本例中,调用是在setter中完成的。
假设您的数据上下文设置正确,在您的SettingsView
视图中,您现在将有:
...
<Button Command="{Binding TestConnectionCommand}"> Test </Button>
<TextBlock Text="Connection failed!">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding ConnectionSuccessfull}" Value="True">
<Setter Property="Text" Value="Connection successful!" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
...
我使用了一个带有数据触发器的文本块来显示连接状态,但是您可以使用布尔值和数据触发器来做任何您想做的事情。也许显示一个模态对话框或其他东西。
更好的是使用某种依赖注入来注入你的数据库服务和其他依赖,同时使数据库代码异步,这样你的UI就不会冻结,但这将是未来你要弄清楚的。