我有一个WPF应用程序,它旨在从数据库中异步获取信息,当它最终被检索时,将其显示在各种文本框中。
我的asynchonrous查找工作正常,但我是WPF和MVVM的新手。以前,我没有正确分离我的UI和我的模型,因此很容易告诉文本框显示“查询...”,直到实际数据可用。
现在,我已尝试将模型与用户界面分开,我不再确定将其放在何处。
可以接受(从MVVM的角度来看)让UI按钮更新各种UI文本框,在其自己的点击事件中说“查询...”(同时调用最终将更新属性的ViewModel方法,这些文本框是绑定,然后应该用实际数据替换它们?)
如果没有,那么 - 应该 - 这个逻辑去了。 (我也知道命令比onClick事件更好,但请一次一件事:))
这就是我现在正在考虑的事情:
class MainWindow
{
private ViewModel _viewModel;
doStuffButton_Click(object sender, RoutedEventArgs e)
{
textBox1.Text = "Querying..."
textBox2.Text = "Querying..."
// ... and so on in a foreach looop probably.
_viewModel.asyncGoAndLookupStuff(); // starts a Task that
// updates properties that the textBoxes are bound to
// and fires the appropriate PropertyChanged events.
}
}
这是好习惯吗?如果没有,我怎么能在ViewModel中获得这种功能呢?我已经开始了,我遇到了严重的问题,将ViewModel的属性设置为各种字符串,同时仍然能够在更新时将它们设置为模型中的相应(非字符串)值。这可以用ValueConverter(和空值)来解决吗?我真的不知道如何去做,但我还没有真正看过转换器。
我已经看到很多关于UI如何不知道ViewModel中实际发生了什么的东西,以及它应该如何基本上只是笨拙地坐在那里显示ViewModel目前暴露的内容,但原因我问这个问题是我不确定多少谨慎。
编辑:
我不认为这是相关的,但从下面的答案可能是。在模型中,我有类似的东西(更大/更复杂)
class DataItem
{
// assume a ctor to set it up here.
public int SmallNumber { get; private set; }
public int BigNumber { get; private set;}
}
class Model
{
public DataItem Foo;
public DataItem Bar;
}
和文本框(当前)都绑定到ViewModel.Model.Foo.SmallNumber或ViewModel.Model.Bar.BigNumber。
我是否真的需要在ViewModel中为模型的每个子元素创建单独的属性(例如,ViewModel.FooSmallNumber作为字符串属性),以便它们都可以是字符串类型?如果可能的话,我想将这种相对简单的绑定直接保存到模型中,但仍然会让文本框有时显示有用的字符串。 (我现在无法依赖模型的属性在查询时为null,它们可能包含过时的信息......我可以更改它以清除它们,我猜。)
答案 0 :(得分:2)
您的视图模型不应该知道您的控件。因此,它不应更新任何控件属性。以下是在查询时更改文本的方法:
在视图模型中创建string
依赖项属性,并将其绑定到第一个文本框的文本。在异步加载之前和异步方法完成之后更改此字符串。
或者,使用BusyControl并将其Busy
属性绑定到视图模型中的布尔值,您可以在异步调用之前和之后更新。
最后,遵循MVVM模式并不是要谨慎,而是关于逻辑和表示的绝对分离。问自己:“如果我改变我的观点(通过重命名控件,删除某些部件等),它会影响我的视图模型吗?如果答案是肯定的,那么你的系统中就有紧密耦合的部分。
答案 1 :(得分:2)
如果您只在开始时查询一次,则可以设置Text属性的defaultvalue,如下所示:
<TextBox Text="{Binding MyProperty, TargetNullValue="Querying...", FallbackValue="Querying..."}" />
如果您需要在下一个查询中返回“查询...”文本,请在所有受影响的文本框上设置文本框样式,并使用触发器设置文本,只要VM中的某些布尔值(IsQuerying)为设为true。这将覆盖你的绑定,除非它被设置为TwoWay(即使那时我也不确定......)所以也许设置另一个属性是可行的方式(如IsEnabled或Opacity)。
<Style x:Key="QueryingStyle" TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName = ThisVrEditorView, Path=DataContext.IsQuerying}">
<Setter Property="Text" Value="Querying..."/>
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
答案 2 :(得分:1)
您可以而且应该在虚拟机中完成所有这些操作。
您可以向VM添加字符串属性Item1(带有更改通知),并在启动时将其设置为“查询...”。 Async方法完成后,使用Dispatcher.Invoke(或ThreadingContext)将新数据复制到属性。