INotifyPropertyChanged 已实施,但文本块仅更新一次
INotifyPropertyChanged implemented, but textblock only updates once
@grek40回答
我已经在我的项目中实现了 INotifyPropertyChanged
,并且对于其他一切它都工作正常,但是对于这个变量绑定的文本块,它只在主 window 加载事件上更新一次。
我可能只是遗漏了一些小细节,请帮忙!
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));
}
}
}
您的逻辑没有问题,一切看起来都正确附加并且值按预期更新。唯一的问题是您的 UI 没有更新它,因为您正在主线程上执行 For 循环,不幸的是,它阻塞了您所有的 UI 更新。
所以你可以
- 运行
ExecuteAndTimeAction
作为后台操作使用 Backgroundworker/[Task Library][1]
.
2.Use 调度程序刷新您的 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}";
}
第二种方法是破解,它仍然会冻结
UI 但会为您完成工作。
我建议你采用第一种方法,
它好多了,但需要努力实施。
基本上,您有两个 InvocationServiceHandler
实例。一个在 DataContext
内为 ish = new InvocationServiceHandler()
,另一个在 MyViewModel
内为 private readonly InvocationServiceHandler _invocationServiceHandler = new InvocationServiceHandler();
因此显示 ish.IsDoingWork
并更新 hksVm._invocationServiceHandler.IsDoingWork
之类的内容。这不是很清楚,因为 HentKundeStatusVm
和 MyViewModel
在问题中并不是一回事。
应该可以通过服务处理程序的构造函数注入来解决这种情况:
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
};
然后您就有了可用于绑定和执行的相同处理程序实例。
@grek40回答
我已经在我的项目中实现了 INotifyPropertyChanged
,并且对于其他一切它都工作正常,但是对于这个变量绑定的文本块,它只在主 window 加载事件上更新一次。
我可能只是遗漏了一些小细节,请帮忙!
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));
}
}
}
您的逻辑没有问题,一切看起来都正确附加并且值按预期更新。唯一的问题是您的 UI 没有更新它,因为您正在主线程上执行 For 循环,不幸的是,它阻塞了您所有的 UI 更新。
所以你可以
- 运行
ExecuteAndTimeAction
作为后台操作使用Backgroundworker/[Task Library][1]
.
2.Use 调度程序刷新您的 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}";
}
第二种方法是破解,它仍然会冻结 UI 但会为您完成工作。 我建议你采用第一种方法, 它好多了,但需要努力实施。
基本上,您有两个 InvocationServiceHandler
实例。一个在 DataContext
内为 ish = new InvocationServiceHandler()
,另一个在 MyViewModel
内为 private readonly InvocationServiceHandler _invocationServiceHandler = new InvocationServiceHandler();
因此显示 ish.IsDoingWork
并更新 hksVm._invocationServiceHandler.IsDoingWork
之类的内容。这不是很清楚,因为 HentKundeStatusVm
和 MyViewModel
在问题中并不是一回事。
应该可以通过服务处理程序的构造函数注入来解决这种情况:
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
};
然后您就有了可用于绑定和执行的相同处理程序实例。