【问题标题】:Why isn't this Silverlight attached property working?为什么此 Silverlight 附加属性不起作用?
【发布时间】:2010-02-20 19:09:38
【问题描述】:

我正在尝试在我的 Silverlight 3 应用程序中使用 MVVM 模式,但在绑定到工作视图模型的命令属性时遇到问题。首先,我尝试添加一个名为 ClickCommand 的附加属性,如下所示:

public static class Command
{
    public static readonly DependencyProperty ClickCommandProperty = 
        DependencyProperty.RegisterAttached(
            "ClickCommand", typeof(Command<RoutedEventHandler>), 
            typeof(Command), null);

    public static Command<RoutedEventHandler> GetClickCommand(
        DependencyObject target)
    {
        return target.GetValue(ClickCommandProperty) 
            as Command<RoutedEventHandler>;
    }

    public static void SetClickCommand(
        DependencyObject target, Command<RoutedEventHandler> value)
    {
        // Breakpoints here are never reached
        var btn = target as ButtonBase;
        if (btn != null)
        {
            var oldValue = GetClickCommand(target);
            btn.Click -= oldValue.Action;

            target.SetValue(ClickCommandProperty, value);
            btn.Click += value.Action;
        }
    }
}

通用 Command 类是委托的包装器。我只是包装了一个委托,因为我想知道是否有一个属性的委托类型是最初对我不起作用的原因。这是那个类:

public class Command<T> /* I'm not allowed to constrain T to a delegate type */
{
    public Command(T action)
    {
        this.Action = action;
    }

    public T Action { get; set; }
}

这是我使用附加属性的方式:

<Button u:Command.ClickCommand="{Binding DoThatThing}" Content="New"/>

语法似乎被接受,我认为当我使用字符串属性类型测试所有这些时,效果很好。这是绑定到的视图模型类:

public class MyViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    public Command<RoutedEventHandler> DoThatThing
    {
        get
        {
            return new Command<RoutedEventHandler>(
                (s, e) => Debug.WriteLine("Never output!"));
        }
    }
}

Command 属性中包含的委托永远不会被调用。此外,当我在附加属性的 getter 和 setter 中放置断点时,它们永远不会到达。

在尝试隔离问题时,我将属性类型更改为字符串; getter 和 setter 中的断点也从未到达,但在其中抛出异常确实会导致应用程序终止,所以我认为这是框架的怪癖。

为什么这些东西不起作用?我也欢迎将事件处理程序绑定到视图模型的替代方法,希望是更简单的方法。

【问题讨论】:

  • 有趣。您是否尝试过使用非通用版本?
  • 好主意。但是,我刚刚尝试创建一个RoutedEventCommand 类并用它替换Command&lt;RoutedEventHandler&gt; 的实例,并且行为是相同的。

标签: c# data-binding silverlight-3.0 mvvm delegates


【解决方案1】:

这里至少有两个问题。

首先,您依赖于正在执行的 SetXxx 方法。当从 XAML 设置 DP 时,不执行依赖属性的 CLR 包装器(属性设置器或 SetXxx 方法);相反,WPF 直接设置内部管理的 DP“槽”的值。 (这也解释了为什么您的断点从未被命中。)因此,您处理更改的逻辑必须始终发生在 OnXxxChanged 回调中,而不是在 setter 中; WPF 将在属性更改时为您调用该回调,无论该更改来自何处。因此(示例取自一个稍微不同的命令实现,但应该会给你这个想法):

// Note callback in PropertyMetadata

public static readonly DependencyProperty CommandProperty =
  DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(Click),
  new PropertyMetadata(OnCommandChanged));

// GetXxx and SetXxx wrappers contain boilerplate only

public static ICommand GetCommand(DependencyObject obj)
{
  return (ICommand)obj.GetValue(CommandProperty);
}

public static void SetCommand(DependencyObject obj, ICommand value)
{
  obj.SetValue(CommandProperty, value);
}

// WPF will call the following when the property is set, even when it's set in XAML

private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  ButtonBase button = d as ButtonBase;
  if (button != null)
  {
    // do something with button.Click here
  }
}

其次,即使进行此更改,在尚未设置值的控件上设置 ClickCommand 也会导致异常,因为 oldValue 为 null,因此 oldValue.Action 会导致 NullReferenceException。您需要检查这种情况(您还应该检查 newValue == null 虽然这不太可能发生)。

【讨论】:

  • 非常感谢。执行更改的处理程序解决了问题。
  • @itowlson,这是一个很好的解释。让我明白了一些事情,这让我很头疼。谢谢
猜你喜欢
  • 2013-08-17
  • 1970-01-01
  • 1970-01-01
  • 2013-03-03
  • 2019-05-22
  • 1970-01-01
  • 2019-02-19
  • 2017-02-16
  • 1970-01-01
相关资源
最近更新 更多