我有一个带登录视图模型的登录视图,遇到一个让我疯狂的问题。请注意,我正在使用MVVM Light。
在viewmodel中,我有三个可见性属性,应根据viewmodel的状态隐藏视图中的元素。
我的属性如下所示,全部触发PropertyChangedEvent:
public Visibility ErrorPanelVisibility
public Visibility LoginPanelVisibility
public Visibility LoadingPanelVisibility
绑定看起来像这样(省略边距等):
<Border Visibility="{Binding ErrorPanelVisibility}">
<StackPanel Visibility="{Binding LoginPanelVisibility}">
<StackPanel Visibility="{Binding LoadingPanelVisibility}">
当用户按下登录按钮时,在viewmodel中执行此方法的命令会更改可见性:
private void ExecuteLogin()
{
ErrorPanelVisibility = Visibility.Collapsed;
LoginPanelVisibility = Visibility.Collapsed;
LoadingPanelVisibility = Visibility.Visible;
// This method takes a few seconds to complete.
Login(errorMessage =>
{
if (!String.IsNullOrEmpty(errorMessage))
{
ErrorMessage = errorMessage;
ErrorPanelVisibility = Visibility.Visible;
LoginPanelVisibility = Visibility.Visible;
LoadingPanelVisibility = Visibility.Collapsed;
}
else
{
DialogResult = true;
}
});
}
问题是面板没有折叠或在视图中更改为可见,更改不会反映在视图中。当我将可见性设置为Visible但不是Collapsed时,会触发PropertyChangedEvent并且ErrorPanel正在工作。
编辑:登录方法:
private void Login(Action<string> callback)
{
string errorMessage = string.Empty;
if (SelectedServer == null)
{
errorMessage = "Select server."; //TODO: Translate
}
else if (String.IsNullOrEmpty(Username) || String.IsNullOrEmpty(Password))
{
errorMessage = Resources.WrongUsernameOrPassword;
}
else
{
try
{
// Changed name due to customer information
IWCFClient wcfClient = WCFClient.GetInstance();
LoginObj loginObj = wcfClient.Login(Username, Password);
// Removed some cases to simplify, they all set the errorMessage.
switch (loginObj.LoginStatus)
{
case LoginObj.LoginState.AlreadyLogedin:
errorMessage = Resources.UserAlreadyLoggedOn;
break;
default:
errorMessage = Resources.ErrorOccurred;
break;
}
}
catch (Exception e)
{
errorMessage = e.Message;
}
}
callback(errorMessage);
}
但是,如果我将代码更改为此内容,面板可以正常工作:
bool login = false;
private void ExecuteLogin()
{
if (login)
{
ErrorPanelVisibility = Visibility.Collapsed;
LoginPanelVisibility = Visibility.Collapsed;
LoadingPanelVisibility = Visibility.Visible;
}
else
{
ErrorPanelVisibility = Visibility.Visible;
LoginPanelVisibility = Visibility.Visible;
LoadingPanelVisibility = Visibility.Collapsed;
}
login = !login;
}
这里有没有明显的错误,或者有人能发现我做错了吗?
答案 0 :(得分:5)
另外。我建议你在ViewModels中避免使用UI类型。尝试使用bool值并使用BoolToVisibilityConverter。
P.S。确保为控件设置DataContext。
答案 1 :(得分:1)
您正在UI线程上完成所有工作,并且面板永远不会有机会更新。 使用BackgroundWorker来保持UI响应:
http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
应该更像是:
1)更改一些影响UI绑定的属性
2)启动工作线程
3)返回对UI线程的控制以允许更新
4)通过更改影响UI绑定的一些属性来响应正在完成的工作线程
答案 2 :(得分:0)
1)你错过了Binding.Mode。因此,您必须将模式添加到xaml绑定中,例如<Border Visibility="{Binding ErrorPanelVisibility, Mode=OneWay}">
。
2)ViewModel必须实现INotifyPropertyChange
以将更改从自身更改为View。
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public Visibility ErrorPanelVisibility
{
get { return this._errorPanelVisibility; }
set
{
if (this._errorPanelVisibility == value)
{
return;
}
this._errorPanelVisibility = value;
OnPropertyChanged("ErrorPanelVisibility");
}
}
我也同意Dima Martovoi,你不应该将View与ViewModel混合使用。至少有两种方法可以执行此操作,第一种方法是与查看点击事件相关的移动代码,或者您可以使用Messenger从ViewModel
向View
发送消息