UpdateSource 无法在 CoerceValueCallback 中工作

2024-01-10

我的总体目标是创建一个 TwoWay Attached DependencyProperty (或 OneWayToSource),始终将其绑定源更新为特定值。在我的现实世界场景中,这是一个不恒定的对象,取决于它所附加的对象。

我的示例涉及以下模型和视图:

public class ViewModel : ViewModelBase
{
  public ViewModel()
  {
    firstContainer = new Container();
    otherContainer = new Container();
    TestContainer = firstContainer;

    SwitchContainersCommand = new DelegateCommand(SwitchContainers);
  }

  private Container firstContainer;
  private Container otherContainer;

  public Container TestContainer 
  {
    get { return testContainer; }
    set { Set(ref testContainer, value); }
  }
  private Container testContainer;

  public ICommand SwitchContainersCommand { get; }
  private void SwitchContainers()
  {
    if (TestContainer == firstContainer)
      TestContainer = otherContainer;
    else
      TestContainer = firstContainer;
  }
}

public class Container : ViewModelBase
{
  public object TestTarget
  {
    get { return testTarget; }
    set { Set(ref testTarget, value); }
  }
  private object testTarget = new SampleValue();
}

public class SampleValue
{
  public override string ToString()
  {
    return "unprovided value";
  }
}

View:

<Window>
  <Window.DataContext>
    <local:ViewModel/>
  </Window.DataContext>
  <StackPanel local:ProvideToSource.Target="{Binding TestContainer.TestTarget, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
    <TextBlock><Run Text="ToString: "/><Run Text="{Binding TestContainer.TestTarget}"/></TextBlock>
    <Button Command="{Binding SwitchContainersCommand}" HorizontalAlignment="Left">
      <TextBlock Text="Switch"/>
    </Button>
  </StackPanel>
</Window>

值得注意的是,我们有一个附加财产local:ProvideToSource.Target,它绑定到TestContainer.TestTarget双向。

现在,代码为ProvideToSource并不太复杂。在强制值上,它会尝试将属性设置为其所需的值。在初次运行时,这工作得很好。

public static class ProvideToSource
{
  private const string Target = nameof(Target);
  public static DependencyProperty TargetProperty = DependencyProperty.RegisterAttached(nameof(Target), typeof(object), typeof(ProvideToSource), new PropertyMetadata(null, null, CoerceValue));

  public static object GetTarget(DependencyObject dependent)
  {
    return dependent.GetValue(TargetProperty);
  }

  public static void SetTarget(DependencyObject dependent, object value)
  {
    dependent.SetValue(TargetProperty, value);
  }

  private static readonly object TestValue = new ProvidedObject();

  private static object CoerceValue(DependencyObject dependent, object value)
  {
    if (value != TestValue)
    {
      dependent.SetValue(TargetProperty, TestValue);
    }

    return TestValue;
  }
}

public class ProvidedObject
{
  public override string ToString()
  {
    return "provided me";
  }
}

理想情况下,当绑定设置为新源(不是最新的)时,它应该将源更新为我们想要的值。 然而,它只是在初始创建时这样做。

当应用程序启动并显示窗口时,文本显示所需的“由我提供”;但是,当单击按钮并交换 TestContainer 时,绑定会重新评估它的强制值(因为它检测到更改),但是当SetValue被调用,后备属性未更新,文本显示“未提供的值”

这种行为有原因吗?我该如何解决它?我尝试过很多事情;但是,在初始化绑定后,我似乎无法从附加属性中成功设置源值。请注意,我已经在使用和不使用 PropertyChangedCallback 的情况下尝试过此操作,但我似乎也无法帮助解决问题。

此外,显式调用BindingOperations.GetBindingExpression(dependent, TargetProperty)?.UpdateSource();CoerceValue 或 PropertyChanged 回调中也无法执行。


我相信我终于找到了最好的解决方案。 我最大的知识空白之一是关于调度程序系统;虽然我不认为我遇到的所有问题都是由错误的线程引起的,但我宁愿安全也不愿后悔。

以下部分代码定义了一个双向绑定属性,该属性始终将其所需值推回到绑定的源属性 - 如果源通知绑定更新,则绑定将循环并将源更新回所需值应该 - 有效地提供一种将任意信息从 View 传递回 ViewModel 的可靠方法。

在初次运行时,CoerceValueCallback将初始化属性的所需值,并将其存储在私有 Cache 依赖项属性中,并强制绑定更新为该值。在所有后续运行中,PropertyChangedCallback将检查该属性是否被更改为任何不是所需值的值,并将强制绑定回其所需值(从缓存依赖项属性加载)。

public static class ProvideToSource
{
  private const string Target = nameof(Target);
  public static DependencyProperty TargetProperty = DependencyProperty.RegisterAttached(nameof(Target), typeof(object), typeof(ProvideToSource), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ValueChanged, CoerceValue, true, UpdateSourceTrigger.Explicit));

  public static object GetTarget(DependencyObject dependent)
  {
    return dependent.GetValue(TargetProperty);
  }

  public static void SetTarget(DependencyObject dependent, object value)
  {
    dependent.SetValue(TargetProperty, value);
  }

  private const string Cache = nameof(Cache);
  private static DependencyProperty CacheProperty = DependencyProperty.RegisterAttached(nameof(Cache), typeof(object), typeof(ProvideToSource));

  private static object CoerceValue(DependencyObject dependent, object value)
  {
    if (dependent.GetValue(CacheProperty) == null)
    {
      value = GetOrCreateCachedValue(dependent);
      dependent.Dispatcher.Invoke(() => UpdateValueAndBindings(dependent, value));
    }

    return value;
  }

  private static void ValueChanged(DependencyObject dependent, DependencyPropertyChangedEventArgs args)
  {
    var cachedValue = GetOrCreateCachedValue(dependent);
    if (args.NewValue != cachedValue)
    {
      dependent.Dispatcher.Invoke(() => UpdateValueAndBindings(dependent, cachedValue));
    }
  }

  private static void UpdateValueAndBindings(DependencyObject dependent, object value)
  {
    var bindingExpression = BindingOperations.GetBindingExpression(dependent, TargetProperty);
    if (bindingExpression != null && bindingExpression.Status != BindingStatus.Detached)
    {
      bindingExpression?.UpdateTarget(); //This call seems out of place but is required
      SetTarget(dependent, value);
      bindingExpression?.UpdateSource();
    }
    else
    {
      SetTarget(dependent, value);
    }
  }

  private static object GetOrCreateCachedValue(DependencyObject dependent)
  {
    var item = dependent.GetValue(CacheProperty);
    if (item == null)
    {
      item = new ProvidedObject();  // Generate item here
      dependent.SetValue(CacheProperty, item);
    }
    return item;
  }
}

使用此代码就位TargetProperty问题中的定义将确保当前ViewModel.TestContainer.TestTarget始终与实例保持同步ProvidedObject.

初始设置是在CoerceValueCallback确保初始值被设置,因为PropertyChangedCallback如果绑定到 null 值,则在首次运行依赖项属性时可能不会设置。

后续组在中完成PropertyChangedCallback确保依赖属性在尝试立即将其更改回来之前已经认为自己已完全更新,而不是尝试在强制过程中将其强制返回。另外,如UpdateTarget()在我们可以之前需要调用UpdateSource()(由于当原始源对象被不同的源对象替换时出现一些异常行为),我们依赖PropertyChangedCallback过滤掉该调用的影响,因为它不会认为绑定正在改变值,并且不会导致无限递归 - 调用UpdateTarget()从内部CoerceValueCallback导致无限递归。

可以针对任何不同类型的依赖属性修改代码,但是object样板较少。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UpdateSource 无法在 CoerceValueCallback 中工作 的相关文章

  • 带有 ASP.NET 按钮回发的 jQuery UI 对话框

    我的 ASP NET 页面上有一个运行良好的 jQuery UI 对话框 jQuery function jQuery dialog dialog draggable true resizable true show Transfer hi
  • 在 LINQ 查询中进行转换

    是否可以在 LINQ 查询中进行强制转换 为了编译器的缘故 下面的代码并不糟糕 但最好将其放入一个查询中 Content content dataStore RootControl as Controls Content List
  • copy_from_user() 错误:目标大小太小

    我正在为内核模块编写 ioctl 处理程序 我想从用户空间复制数据 当我编译禁用优化的代码时 O0 gflags 编译器返回以下错误 include linux thread info h 136 17 error call to bad
  • 未找到 DEADLINE 调度策略

    我想在 C 中实现 DEADLINE 调度策略 我知道该功能已实现Linux 3 14 10我正在使用 Ubuntu 14 04Linux 3 17 0 031700 lowlatency 201410060605 SMP PREEMPT这
  • 使用管道在父级和子级之间传递整数值

    我对如何正确使用 pipeline 在两个进程之间传递整数值有点困惑 在我的程序中 我首先创建一个管道 然后分叉它 我假设我有 两个 管道 据我了解 这是我的任务 我的父母通过 for 循环检查某个操作的整数值 i 增加计数变量 并将值保存
  • 如何在 C++ 中为指针“this”赋值

    在函数中 如何分配this一个新的价值 您可以分配对象this点于 this XY 但你不能分配直接值this this XY Error Expression is not assignable
  • 每个元素的 asp.net Web 表单自定义错误消息

    我创建了一个 Web 应用程序 表单 以及后端 SQL 插入和查询 目前我正在显示所有用户错误消息 div style padding 1em div
  • 从结构调用 C++ 成员函数指针

    我找到了有关调用 C 成员函数指针和调用结构中的指针的信息 但我需要调用结构内部存在的成员函数指针 但我无法获得正确的语法 我在类 MyClass 的方法中有以下代码片段 void MyClass run struct int MyClas
  • 在两个屏幕上拉伸 WPF 窗口

    我是 WPF 的初学者 我希望我的应用程序能够跨越两个具有相同分辨率的屏幕 我需要一个示例代码应用程序 因为我不知道从哪里开始 谢谢 The Screen课程将为您提供有关所有屏幕的信息 在之前没有实际做过的情况下 我会尝试以下操作 int
  • 在 C# 中解析 JS Date.toIsoString

    我需要将 JS 日期存储为 ISO 8601 日期 我目前正在从格式为 2019 06 22T00 00 00 000Z 的表单中获取日期 正如 JS 的 toIsoString 方法所期望的那样 当这个日期传递到我的 API 控制器时 我
  • 加载 QPixmap 数据的更好方法

    更好的方法来做到这一点 没有QImage QImage image width height QImage Format RGB888 memcpy image bits m frameRGB gt data 0 height width
  • 从图像创建半透明光标

    是否可以从图像创建光标并使其半透明 我目前正在拍摄自定义图像并覆盖鼠标光标图像 如果我可以将其设为半透明 那就太好了 但不是必需的 销售人员喜欢闪亮的 目前正在做这样的事情 Image cursorImage customImage Get
  • DateTime.ParseExact - 为什么 yy 变成 2015 而不是 1915

    为什么 NET 假定以下年份是 2015 年 而不是 1915 年 var d DateTime ParseExact 20 11 15 dd MM yy new CultureInfo en GB 我想 它会尝试接近 但其背后是否有合理的
  • 为什么连续抛出 2 个异常不会生成无法访问的代码警告?

    为什么以下代码行不会创建编译器警告 void Main throw new Exception throw new Exception 据我所知 编译器应该通知您无法到达第二个抛出异常 这显然是一个编译器错误 它是在 C 3 0 中引入的
  • 将 AutomationID 与 ListView 结合使用

    我正在尝试将 AutomationId 附加到列表视图中的项目 理想情况下 将项目名称绑定到显示的项目
  • fgets溢出后如何清除输入缓冲区?

    当输入字符串超出其预定义限制时 我遇到了 fgets 的小问题 以下面的例子为例 for index 0 index lt max index printf Enter the d string index 1 if fgets input
  • 在一个解决方案中调用不同项目的方法

    1 个解决方案中有 3 个项目 我对第一个项目中的主文件进行的主要操作 但是我需要调用第三个项目中的方法并使用类 例如 第三个项目有 public DataClasses1DataContext base global WindowsFor
  • 使用未命名命名空间而不是静态命名空间

    我可以假设在未命名命名空间中声明的对象相当于static namespace int x 1 static int x 2 FWIK 在这两种情况下 x将具有静态存储期限和内部链接 声明为的对象的所有规则也是如此static适用于未命名名称
  • 异步/等待 - 是*并发*吗?

    我一直在考虑 C 5 中新的异步内容 并且出现了一个特殊问题 据我了解 await关键字是一个简洁的编译器技巧 语法糖来实现连续传递 http en wikipedia org wiki Continuation passing style
  • 稀疏矩阵超定线性方程组c/c++库

    我需要一个库来解决 Ax b 系统 其中 A 是一个非对称稀疏矩阵 每行有 8 个条目 而且可能很大 我认为实现双共轭梯度的库应该没问题 但我找不到一个有效的库 我尝试过 iml 但 iml sparselib 包中缺少一些标头 有小费吗

随机推荐