【问题标题】:Passing a Property by Reference [duplicate]通过引用传递属性[重复]
【发布时间】:2018-07-09 11:34:23
【问题描述】:

我有许多进度条,每个进度条都绑定到它们自己的 int 属性,用于更新它们的进度。在幕后,我有一个多次运行的方法,它执行我的处理(循环数据),我用它来更新更新进度条的属性。

我遇到的问题是您无法将属性传递给该方法。我可以创建该方法的多个副本,每个副本都引用每个进度条的特定属性,但这需要大量重复的代码。有人会建议我如何将属性传递给方法?

基本上这是我想要做的,但显然通过 ref 传递属性是行不通的。

Method(fileList, errorList, _ViewModel.ProgressBarProp1);
Method(fileList, errorList, _ViewModel.ProgressBarProp2);
...

private static void Method(IEnumerable<string> fileList, List<Tuple<string, 
    string>> errorList, ref int PropertyInt)
{
    foreach (var file in fileList)
    {
        if (!File.Exists(file))
        {
            errorList.Add(new Tuple<string, string>(file,
                " does not exist in the folder"));
        }

        PropertyInt++;
    }
}

我见过this question,但这涉及字符串,我无法为整数场景实施任何解决方案。

更新

在下面实施 Mike 的解决方案是允许从方法内访问该属性,但是我在同时运行它的方式上有一些奇怪的行为。

我正在实现这样的代码,两个方法同时运行但递增相同的属性值(即,两者都关联到同一个进度条)。

var taskList = new List<Task>
{
    Task.Run(() =>
    {
        Method1(fileList, errorList, p => _ViewModel.ProgressBarProp1 = p);
    }),
    Task.Run(() =>
    {
        Method2(fileList, errorList, p => _ViewModel.ProgressBarProp1 = p);
    })
};
await Task.WhenAll(taskList.ToArray());

但是,似乎只有一个正在更新。是什么原因?有解决办法吗?

【问题讨论】:

  • @Clemens 不,我想在方法运行时动态更新属性,而不是在方法完成时更新。
  • 您可以将属性和属性名称作为字符串传递。然后在您更新属性(PropertyInt)之后,您可以使用您传递的名称调用 PropertyChanged。但是如果你想在 UI 中看到更新,你的方法应该在单独的线程中运行。

标签: c# wpf


【解决方案1】:

将您的 ref int 替换为 Action&lt;int&gt; 代表。当您取得进展时,使用新的进度值调用委托。在调用站点,传入一个 lambda,它采用新的进度值并将其分配给您的属性。

Method(fileList, errorList, p => _ViewModel.ProgressBarProp1 = p);

private static void Method(
    IEnumerable<string> fileList,
    List<Tuple<string, string>> errorList,
    Action<int> reportProgress)
{
    var progress = 0;

    foreach (var file in fileList)
    {
        if (!File.Exists(file))
        {
            errorList.Add(new Tuple<string, string>(file,
                " does not exist in the folder"));
        }

        reportProgress?.Invoke(++progress);
    }
}

如果您将响应 UI 线程上的进度但从另一个线程报告它,您应该确保您的回调将自己编组到 UI 线程。您可以使用简单的辅助方法来完成此操作:

public static class DispatcherHelper
{
    public static Action<T> MakeInvoker<T>(
        this Dispatcher dispatcher,
        Action<T> action,
        DispatcherPriority priority = DispatcherPriority.Normal)
    {
        return o => dispatcher.BeginInvoke(priority, action, o);
    }
}

然后,如下修改您的调用:

var dispatcher = Application.Current.Dispatcher;
var reportProgress = dispatcher.MakeInvoker<int>(p => _ViewModel.ProgressBarProp1 = p);

await Task.WhenAll(
    Task.Run(() => Method1(fileList, errorList, reportProgress)),
    Task.Run(() => Method2(fileList, errorList, reportProgress)));

【讨论】:

  • 谢谢你,请看我上面的更新。
  • 听起来像是一场简单的数据竞赛。我用一些指导更新了我的答案。
  • 澄清一下,增加一个属性不是原子操作,如果两个线程同时做,你会得到不可预知的结果。
  • 我创建了一个简化的解决方案来测试它。添加一个进度条,将最大值绑定到 TextMax,将值绑定到 TextProgress,您会注意到进度条只填充到一半左右。完整代码在这里:fnpaste.com/bmrE
猜你喜欢
  • 2014-11-08
  • 2011-01-23
  • 1970-01-01
  • 1970-01-01
  • 2017-08-12
  • 1970-01-01
  • 2010-11-27
相关资源
最近更新 更多