简体   繁体   English

附属物。如何在PropertyChangedCallback中使用SetValue

[英]Attached Property. How to use SetValue in PropertyChangedCallback

I need Attached Property that sets focus to UIElement from ViewModel. 我需要附加属性,将焦点设置为ViewModel中的UIElement。 I've created Attached property and in PropertyChangedCallback I set focus to UIElement. 我已经创建了Attached属性,在PropertyChangedCallback中我将焦点设置为UIElement。

private static void VoySetFocusChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
  if (o is UIElement)
  {
    if ((bool)e.NewValue)
    {
      (o as UIElement).Focus();
      (o as UIElement).SetValue(VoySetFocusProperty, false);
    }
  }
}

but I want it to work like trigger in shotgun. 但是我希望它像霰弹枪中的触发器一样工作。 I set true to Test in ViewModel ... it invokes PropertyChangedCallback in MyAttachedProperties class,sets focus to UIElement 我在ViewModel中设置为true ...它在MyAttachedProperties类中调用PropertyChangedCallback,将焦点设置为UIElement

((o as UIElement).Focus();

and value of Test in ViewModel returns to false 并且ViewModel中的Test值返回false

((o as UIElement).SetValue(VoySetFocusProperty, false);)

Everything seems to work fine but SetValue doesn't change my value in ViewModel back. 一切似乎都运行正常,但SetValue不会改变我在ViewModel中的值。

Full code: 完整代码:

View: 视图:

<Window x:Class="WpfAttachedProperty.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfAttachedProperty"
    Title="MainWindow" Height="127" Width="316">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
    </Grid.RowDefinitions>
    <TextBox local:MyAttachedProperties.VoySetFocus="{Binding Path=Test,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Text="Focus Me"/>
    <Button Grid.Row="1" Content="Click to Focus" HorizontalAlignment="Left" Margin="10" VerticalAlignment="Top" Width="75" Command="{Binding MyCommand}" />
</Grid>

Code Behind: 代码背后:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfAttachedProperty
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow:Window
{
  public MainWindow()
  {
    InitializeComponent();
    DataContext = new ViewModel();
  }
}

/// <summary>
/// Command Class
/// </summary>
public class DelegateCommand:ICommand
{
  private readonly Action _action;

  public DelegateCommand(Action action)
  {
    _action = action;
  }

  public void Execute(object parameter)
  {
    _action();
  }

  public bool CanExecute(object parameter)
  {
    return true;
  }

  public event EventHandler CanExecuteChanged
  {
    add
    {
    }
    remove
    {
    }
  }
}

/// <summary>
/// ViewModelClass
/// </summary>
public class ViewModel:INotifyPropertyChanged
{
  private bool _test = false;
  public bool Test
  {
    get
    {
      return _test;
    }
    set
    {
      _test = value;
      this.NotifyPropertyChanged("Test");
    }
  }
  public ICommand MyCommand
  {
    get
    {
      return new DelegateCommand(SetTestToTrue);
    }
  }
  private void SetTestToTrue()
  {
    this.Test = true;
  }

  #region INotifyPropertyChanged
  public void NotifyPropertyChanged(String PropertyName)
  {
    if (this.PropertyChanged != null)
    {
      this.PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
    }
  }
  public event PropertyChangedEventHandler PropertyChanged;
  #endregion
}


public class MyAttachedProperties
{
  public static Object GetVoySetFocus(DependencyObject obj)
  {
    return (Object)obj.GetValue(VoySetFocusProperty);
  }

public static void SetVoySetFocus(DependencyObject obj, Object value)
{
  obj.SetValue(VoySetFocusProperty, value);
}

public static readonly DependencyProperty VoySetFocusProperty =
    DependencyProperty.RegisterAttached("VoySetFocus", typeof(bool), typeof(MyAttachedProperties), new UIPropertyMetadata(false, new PropertyChangedCallback(VoySetFocusChanged), new CoerceValueCallback(CoerceVoySetFocus)));

private static object CoerceVoySetFocus(DependencyObject d, object baseValue)
{
  return (bool)baseValue;
}

private static void VoySetFocusChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
  if (o is UIElement)
  {
    if ((bool)e.NewValue)
    {
      (o as UIElement).Focus();
      // Doesn't set Test to false in ViewModel
      (o as UIElement).SetValue(VoySetFocusProperty, false);
    }
  }
}
}
}

Greetings Marko. 问候马可。

Issue is with the line: 问题在于:

((o as UIElement).SetValue(VoySetFocusProperty, false);)

You should instead use SetCurrentValue to set the DP. 您应该使用SetCurrentValue来设置DP。

((o as UIElement).SetCurrentValue(VoySetFocusProperty, false);)

Explanation : 说明

Setting the value directly of any DependencyProperty breaks the binding it with source property. 直接设置任何DependencyProperty的值会破坏它与source属性的绑定。

However, SetCurrentValue doesn't break the binding and push back the value back to the source property. 但是, SetCurrentValue不会破坏绑定并将值推回到source属性。 Explanation from MSDN for SetCurrentValue: MSDN对SetCurrentValue的解释:

This method is used by a component that programmatically sets the value of one of its own properties without disabling an application's declared use of the property. 此方法由组件使用,该组件以编程方式设置其自身属性之一的值,而不禁用应用程序声明的属性使用。 The SetCurrentValue method changes the effective value of the property, but existing triggers, data bindings, and styles will continue to work . SetCurrentValue方法更改属性的有效值,但现有的触发器,数据绑定和样式将继续工作


Moreover, I think setting it in PropertyChanged callback won't propagate that back to Viewmodel if do the operation synchronously since it reaches to callback from Viewmodel property setter only. 此外,我认为在PropertyChanged回调中设置它不会将其传播回Viewmodel,如果它同步执行操作,因为它仅从Viewmodel属性setter进行回调。

So, what we can do is do this operation asynchronously by enqueuing it on dispatcher using BeginInvoke so that it gets propagate to viewmodel Test property. 因此,我们可以做的是通过使用BeginInvoke将它排入调度程序来异步执行此操作 ,以便它传播到viewmodel Test属性。

(o as UIElement).Focus();
Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate
{
    // Doesn't set Test to false in ViewModel
    (o as UIElement).SetCurrentValue(VoySetFocusProperty, false);
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用 PropertyChangedCallBack - How to use PropertyChangedCallBack 如何在依赖属性的 PropertyChangedCallback 中绑定异步方法? - how to bind async method in PropertyChangedCallback of dependency property? 如何在反射中使用索引属性的SetValue? - How to use SetValue of an Indexed Property in Reflection? 依赖项属性PropertyChangedCallback异常 - Dependency Property PropertyChangedCallback exception treenode的treeview属性。 这个怎么样 - treenode's treeview property. How is this set? 实例化自定义类型时如何触发Dependency属性PropertyChangedCallback - How to fire Dependency property PropertyChangedCallback when instantiating custom type 如何覆盖 WPF ItemsControl 中预定义依赖属性 ItemsSource 的 PropertyChangedCallback - How to Override PropertyChangedCallback of a predefined Dependency Property ItemsSource in a WPF ItemsControl 如何将PropertyChangedCallback添加到基类的依赖项属性? - How can I add a PropertyChangedCallback to a dependency-property of the base class? 我如何在linq中的select语句中使用where子句。 (即在某个物业上。) - How can i use a where clause within a select statement in linq. (ie. on a property.) WPF - 绑定到只读附加属性。 需要解决方案说明 - WPF - binding to read-only attached property. Need solution clarification
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM