已实现INotifyPropertyChanged,但文本块仅更新一次

时间:2018-06-15 06:07:53

标签: c# wpf inotifypropertychanged

来自@ grek40的答案

我已经在我的项目中实现了INotifyPropertyChanged,并且其他一切工作正常,但是对于这个可变边界的文本块,它只在主窗口加载事件上更新一次。

我可能只是在某处遗漏了一些细节,请帮助!

MainWindow.xaml

<TextBlock HorizontalAlignment="Left" Margin="317,161,0,0"  
TextWrapping="Wrap" Text="{Binding ish.IsDoingWork, Mode=OneWay, 
UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top"/>

<Button  Command="{Binding hksVm.HentKundeStatus}" Content="Invoke" 
HorizontalAlignment="Left" Margin="704,139,0,0" VerticalAlignment="Top" 
Width="75"/>

MainWindow.xaml.cs (设置数据上下文)

public MainWindow()    
{
    if (!ValidationHandler.GrantAccess().Equals(3))
    {
        InitializeComponent();
        DataContext = new
        {
            hksVm = new HentKundeStatusVm(),
            ish = new InvocationServiceHandler()
        };
    }
    else
    {
        Close();
    }
}

ViewModel.cs

using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Input;

namespace MyNamespace
{
    public class HentKundeStatusVm : IViewModel
    {
        private ICommand _hentKundeStatus;
        private readonly InvocationServiceHandler _invocationServiceHandler = new InvocationServiceHandler();


        public ICommand HentKundeStatus => HentKundeStatusCommand();

        public ICommand HentKundeStatusCommand()
        {
            if (ValidationHandler.GrantAccess() < 2)
            {
                return _hentKundeStatus ?? (_hentKundeStatus = new RelayCommand(param =>
                           ElapsedTime = _invocationServiceHandler.ExecuteAndTimeAction(
                               () =>
                               {
                                   //web API kaldes asynkront - husk: using System.Net.Http; 
                                   using (var client = new HttpClient().GetAsync("API-url"))
                                   {
                                       client.Result.Content.ReadAsStringAsync();
                                   }
                               }, AntalKald)));
            }
            return null;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void NotifyPropertyChanged(string property)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
        }
    }
}

InvocationServiceHandler.cs

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading.Tasks;
using App005_WebServiceTestingTool_Domain.Annotations;

namespace App005_WebServiceTestingTool_Domain.Handlers
{
    public class InvocationServiceHandler : INotifyPropertyChanged
    {
        // This works on the main_window load event and set the textblock in the view
        private string _isDoingWork = "Currently not working";
        public string IsDoingWork
        {
            get => _isDoingWork;
            set
            {
                _isDoingWork = value;
                NotifyPropertyChanged(nameof(IsDoingWork));
            }
        }

        /// <summary>
        /// Method that invokes action parameter x times in multiple threads (parallel) and returns the elapsed time
        /// </summary>
        /// <param name="action"></param>
        /// <param name="antalKald"></param>
        /// <returns></returns>
        public string ExecuteAndTimeAction(Action action, string antalKald)
        {
            // Here is set the bound variable, and if I debug I can see it getting set to Working...
            IsDoingWork = "Working...";
            var sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < Convert.ToInt32(antalKald); i++)
            {
               action.Invoke();
            }
            sw.Stop();
            // Here I am resetting the variable and again in debug I can see it change, but nothing happens in the view
            IsDoingWork = "";
            return $"Elapsed time: {sw.Elapsed}";
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        public void NotifyPropertyChanged(string property)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
        }
    }
}

2 个答案:

答案 0 :(得分:0)

您的逻辑很好,所有内容都看起来已正确附加,并且值会按预期更新。唯一的问题是你的UI没有更新它,因为你在主线程上执行For循环,不幸的是阻止了所有的UI更新。

所以你可以

  1. 使用ExecuteAndTimeAction
  2. 作为后台操作运行Backgroundworker/[Task Library][1]

    2.使用Dispatcher刷新您的UI消息,例如:

        /// <summary>
        /// Enters the message loop to process all pending messages down to the specified
        /// priority. This method returns after all messages have been processed.
        /// </summary>
        /// <param name="priority">Minimum priority of the messages to process.</param>
        public static void DoEvents(DispatcherPriority priority = DispatcherPriority.Background)
        {
            DispatcherFrame frame = new DispatcherFrame();
            Dispatcher.CurrentDispatcher.BeginInvoke(
                priority,
                new DispatcherOperationCallback(ExitFrame), frame);
            Dispatcher.PushFrame(frame);
        }
        private static object ExitFrame(object f)
        {
            ((DispatcherFrame)f).Continue = false;
            return null;
        }
    

    并致电

            /// <summary>
            /// Method that invokes action parameter x times in multiple threads (parallel) and returns the elapsed time
            /// </summary>
            /// <param name="action"></param>
            /// <param name="antalKald"></param>
            /// <returns></returns>
            public string ExecuteAndTimeAction(Action action, string antalKald)
            {
                // Here is set the bound variable, and if I debug I can see it getting set to Working...
                IsDoingWork = "Working...";
                DoEvent();//flushes the UI msg queue
                var sw = new Stopwatch();
                sw.Start();
                for (int i = 0; i < Convert.ToInt32(antalKald); i++)
                {
                   action.Invoke();
                }
                sw.Stop();
                // Here I am resetting the variable and again in debug I can see it change, but nothing happens in the view
                IsDoingWork = "";
                DoEvent();//flushes the UI msg queue
                return $"Elapsed time: {sw.Elapsed}";
            }
    

    第二种方法是黑客攻击,它仍然会冻结 用户界面,但将为您完成工作。 我建议你选择第一种方法, 这样做会更好,但需要付出努力才能实施。

答案 1 :(得分:0)

基本上,您有两个InvocationServiceHandler个实例。一个位于DataContextish = new InvocationServiceHandler(),另一个位于MyViewModelprivate readonly InvocationServiceHandler _invocationServiceHandler = new InvocationServiceHandler();

显示ish.IsDoingWork,更新hksVm._invocationServiceHandler.IsDoingWork之类的内容。由于HentKundeStatusVmMyViewModel在问题中并不是真的相同,所以并不是很清楚。

应该可以通过服务处理程序的构造函数注入来解决这种情况:

public class HentKundeStatusVm : IViewModel
{
    private readonly InvocationServiceHandler _invocationServiceHandler;

    public HentKundeStatusVm(InvocationServiceHandler ish)
    {
        _invocationServiceHandler = ish;
    }

    // the other stuff
}

然后

// In the MainWindow constructor
var ishInstance = new InvocationServiceHandler();
DataContext = new
{
    hksVm = new HentKundeStatusVm(ishInstance),
    ish = ishInstance
};

然后你有相同的处理程序实例可用于绑定和执行。