viewmodel的可见性无效

时间:2012-10-17 08:16:30

标签: c# wpf mvvm mvvm-light

我有一个带登录视图模型的登录视图,遇到一个让我疯狂的问题。请注意,我正在使用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;
}

这里有没有明显的错误,或者有人能发现我做错了吗?

3 个答案:

答案 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混合使用。至少有两种方法可以执行此操作,第一种方法是与查看点击事件相关的移动代码,或者您可以使用MessengerViewModelView发送消息