MVVM OnpropertyChange UI更改延迟

时间:2018-11-01 08:47:29

标签: wpf mvvm

我有一个comboxbox,虽然它是良性填充的,但我希望它在UI中替换为一条消息,指出正在加载。 我通过使用显示消息的文本框并在视图模型(IsShowAuthComboBox&LoadingAuthenticationMsg)中为两个对象赋予可见性绑定来做到这一点

这是XAML代码

<ComboBox x:Name="ComboBoxAuthSource"
    Grid.Row="3"
    Style="{StaticResource ComboBoxStyle}"
    SelectedItem ="{Binding SelectedAuthenticationSource,UpdateSourceTrigger=PropertyChanged}"
    ItemsSource="{Binding AuthenticationSource,UpdateSourceTrigger=PropertyChanged}"  
    Visibility= "{Binding IsShowAuthComboBox, Converter={StaticResource BoolToVis}}" />

<TextBox x:Name="ComboBoxAuthCover"
    Grid.Row="3" Grid.Column="{StaticResource TableColumn}"
    Style="{StaticResource  FieldBoxStyle }"
    FontSize="12"
    IsReadOnly="True"
    Visibility="{Binding IsShowGettingAuthenticationMsg, Converter={StaticResource BoolToVis}}"
    Text="{Binding LoadingAuthenticationMsg,UpdateSourceTrigger=PropertyChanged,Mode=OneWay,FallbackValue='Loading authentication sources...'}" />

这是viewModel

public bool IsShowAuthComboBox
    {
        set
        {
            if (_isShowAuthenticationComboBox != value)
            {                   
                _isShowAuthenticationComboBox = value;
                OnPropertyChanged("IsShowAuthComboBox");
                OnPropertyChanged("IsShowGettingAuthenticationMsg");
            }              
        }
        get =>_isShowAuthenticationComboBox; 
    }

public bool IsShowGettingAuthenticationMsg => !_isShowAuthenticationComboBox;


 public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            Log.Write(LogClass.General, LogLevel.Debug,
                $"{propertyName} update triggerd",
                _moduleName);
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

此代码是相关流程中发生的第一件事,但是有时我只会在执行的最后一刻才看到它。 在其他时间,它将按预期工作。

我在这里想念什么?

编辑: 这在验证IP的时候也很简单。 这是代码

 public string SelectedServer
        {
            get => _selectedServer;
            set
            {
                lock (_lockObj)
                {               
                    IsShowAuthComboBox = false;
                    if (!IsValideIp(value))

                    //some code
                    IsShowAuthComboBox  = true;
                }
            }

bool IsValideIp(string ip)
{
   //some code

  //calls the server sync
   return RemotingConfigurator.GetServerConfig(ip).isValid;
}

2 个答案:

答案 0 :(得分:1)

您的问题是,您正在设置IsShowAuthComboBox属性并在同一线程上同步调用IsValideIp。而且,一个线程无法同时更新UI和查询数据库。

您应该做的是在后台线程上调用IsValideIp。我不会在属性的设置器中执行此操作,而是在命令中执行此操作。您可能需要阅读@Stephen Cleary's blog post的主题。

答案 1 :(得分:0)

这就是我最终要做的。将UI更改从数据层移到viewModel(SetUiOnWait)

public string SelectedServer
        {
            get => _selectedServer;


    set
        {
            //IsShowAuthComboBox = false;
            SetUiOnWait(true);

            Log.Write(LogClass.General, LogLevel.Debug,
                $"Server changed from {_selectedServer} to {value} by user",
                _moduleName);

            _selectedServer = value;
            OnPropertyChanged();
            // OnPropertyChanged();

            //workaround for when changing servers when a unique  
            //authentication source is selected causes the selected source to be null :\
            if (AuthenticationSource.Any())
            {
                SelectedAuthenticationSource = AuthenticationSource[0];
            }

            Task.Factory.StartNew(() =>
            {
                LoginInfo.SelectedServer = _selectedServer;

            }).ContinueWith((t) =>
            {
                if(t.Exception !=null)
                {
                    ExceptionLog.Write(t.Exception.GetBaseException(),_moduleName);
                }
                RefreshAuthenticationProperties();
                OnPropertyChanged("IsLimitedClinicalUse");
                OnPropertyChanged("IsNotForClinicalUse");
                SetUiOnWait(false);
            });
        }

 public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    dispatcher.Invoke((Action)(() =>
               {
                   //PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));                          
               }));
}

Task.Factory.StartNew()强制在新线程上执行逻辑,并让UI更改等待完成。

并在OnPropertyChange中调用会强制事件由UI线程处理。