1

我有一个带有登录视图模型的登录视图,遇到了一个让我发疯的问题。请注意,我使用的是 MVVM Light。

在视图模型中,我有三个可见性属性,它们应该根据视图模型的状态隐藏视图中的元素。

我的属性长这样,都触发了PropertyChangedEvent:

public Visibility ErrorPanelVisibility
public Visibility LoginPanelVisibility
public Visibility LoadingPanelVisibility

绑定看起来像这样(省略边距等):

<Border Visibility="{Binding ErrorPanelVisibility}">
<StackPanel Visibility="{Binding LoginPanelVisibility}">
<StackPanel Visibility="{Binding LoadingPanelVisibility}">

当用户按下登录按钮时,可见性会通过在视图模型中执行此方法的命令来更改:

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;
                }
            });
}

问题是面板没有被折叠或在视图中更改为可见,更改不会反映在视图中。当我将可见性设置为可见但未折叠时,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;
}

这里有没有明显的错误,或者任何人都可以找出我做错了什么?

4

3 回答 3

5

还。我建议您避免在 ViewModels 中使用 UI 类型。尝试使用 bool 值并使用 BoolToVisibilityConverter。

PS 确保为您的控件设置 DataContext。

于 2012-10-17T08:21:22.127 回答
1

您正在 UI 线程上完成所有工作,并且面板永远没有机会更新。使用 BackgroundWorker 让您的 UI 保持响应:

http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx

它应该更像:

1) 更改一些影响 UI 绑定的属性

2)启动一个工作线程

3) 交还 UI 线程的控制权以允许更新

4) 通过更改一些影响 UI 绑定的属性来响应正在完成的工作线程

于 2012-10-17T09:35:45.797 回答
0

1)你错过了Binding.Mode。因此,您必须将 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将消息ViewModelView.

于 2012-10-17T09:21:31.203 回答