OneWayToSource困境

时间:2013-04-29 13:29:48

标签: wpf xaml wpf-4.0

我正在使用OneWayToSource绑定,似乎它始终将我的source属性设置为null。为什么会这样?它给我带来了麻烦,因为我需要来自source属性中的target属性的值而不是null。

这是我的代码:

MyViewModel.cs:

public class MyViewModel
{
    private string str;

    public string Txt
    {
        get { return this.str; }
        set { this.str = value; }
    }
}

MainWindow.cs:

public MainWindow()
{
    InitializeComponent();
    MyViewModel vm = new MyViewModel();
    vm.Txt = "123";
    this.DataContext = vm;
}

MainWindow.xaml:

<Window x:Class="OneWayToSourceTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:local="clr-namespace:OneWayToSourceTest">
      <Grid>
        <local:MyButton Content="{Binding Path=Txt, Mode=OneWayToSource}"/>
      </Grid>
 </Window>

MyButton.cs:

public class MyButton : Button
{
    public MyButton()
    {
        this.Content = "765";
    }
}

目标属性为MyButton.Content。源属性为MyViewModel.TxtTxt属性应设置为“765”,但它应为null。

为什么我会收到null而不是765?

修改

请查看MyButton构造函数。实际上如果您使用简单的TwoWay它将起作用。我测试了它,它与构造函数中设置的内容无关。它的结果我猜是OneWayToSource

现在解释我是如何使用TwoWay绑定的,我确实通过调用setvalue方法在构造函数中设置了dp的值,但是然后在包装器中或更好地说getter和setter我没有提供任何setter因此为什么我使TwoWay看起来像OneWayToSource。我做了它来测试它的构造函数是否有错。我认为viewmodel中的属性值为765,这就是我对TwoWay绑定的意思。我刚测试它是否是控件构造函数。在构造函数中设置一个值就可以了。

通过隐藏setter我的意思是这个 设置{}

3 个答案:

答案 0 :(得分:8)

Content属性只能设置为单个值,并且用绑定替换值“756”。

正如我在my answer中向您指出的另一个问题,WPF按此顺序运行代码:

  • 正常 - 构造函数在此处运行
  • 的DataBind
  • 渲染
  • 加载

所以首先要做的就是运行MainWindow构造函数。这将创建调用Button的构造函数的按钮,并将Button.Content设置为“765”。

但是在Button的XAML中,您指定了不同的Content属性 - 绑定。因此,您的按钮对象被创建,Content属性设置为“765”,然后Content属性设置为{Binding Path=Txt, Mode=OneWayToSource}

这与做类似的事情相同:

var b = new MyButton();
b.Content = "756";
b.Content = new Binding(...); 

正在替换 Content属性。

(从技术上讲,最后一行应该是b.SetBinding(MyButton.ContentProperty, new Binding(...));来正确绑定值,但是我使用更基本的语法来更容易理解问题。)

循环的下一步是数据绑定,因此评估Content属性的绑定。

由于它是OneWayToSource绑定,因此该属性仅在更改源元素时更新它,并且因为这是第一次评估绑定,所以它将源设置为默认值为此DependencyProperty所以他们已经同步了。

对于Button.Content依赖项属性,默认值为null,因此您的源元素设置为null

您没有看到TwoWay绑定的这种行为,因为DP从绑定中获取它的值而不是使用默认值。

如果您在设置绑定后设置了值,例如在按钮的Loaded事件中,则在设置Button的{{1}时,您会看到源属性正确更新}}

Content

最后,如果您尝试从自定义控件设置void MyButton_Loaded(object sender, EventArgs e) { ((Button)sender).Content = "756"; } 属性的默认值,则需要覆盖该属性的Content,如下所示:

MetaData

这将使// Note that this is the static constructor, not the normal one static MyButton() { ContentProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata("756")); } 属性的默认值为Content而不是"756"

答案 1 :(得分:4)

MyButton在设置DataContext之前由InitializeComponent实例化,因此绑定在其构造运行时不会生效。尝试单击按钮设置一个值。

答案 2 :(得分:1)

  

实际上如果您使用简单的TwoWay它将起作用。

我不能复制这个。给定您提供的代码,如果使用TwoWay绑定模式,则viewmodels Txt属性将等于“123”,它是在MainWindows构造函数中给出的值。

  

我测试了它,它与在构造函数

中设置的内容无关

正如Rachel所指出的,XAML Binding清除了本地值。 运行按钮构造函数代码后,发生绑定。然后,绑定从依赖项属性的元数据中检索它的默认值。在建立绑定后,例如在Loaded事件中,可以通过在更合适的时间设置值来轻松修复此问题。

这个简单的修改将为您提供所需的结果:

public class MyButton : Button
{
    public MyButton()
    {
        this.Loaded += MyButton_Loaded;
    }

    void MyButton_Loaded(object sender, RoutedEventArgs e)
    {
        this.Content = "765";
    }
}

Rachels的回答还提供了另一种方法来完成这项工作(覆盖属性默认元数据)。

  

为什么每次都有人使用OneWayToSource绑定设置   initalizatin后的价值?这对我没有意义。

我认为这对您没有意义的原因是因为您的TwoWay绑定测试不像您认为的那样有效。

使用OneWayToSource绑定:

使用OneWayToSource绑定会发生以下情况:

  1. 您正在其构造函数中将MyButton.Content设置为“123”。
  2. 您正在XAML中设置OneWayToSource绑定。这清除了您设置的值。
  3. 绑定从属性元数据中检索默认属性值(null),并将ViewModel.Txt属性设置为等于此值。
  4. 如果在按钮加载事件中设置MyButton.Content属性,则在上述事件发生之后,将属性设置为您希望的值。

    您可以通过在MyViewModel.Txt属性获取器中放置断点来自行验证。该值将按此顺序设置为“123”,null和“756”。 / p>

    使用TwoWay绑定:

    现在,如果您要更改XAML以使用TwoWay绑定,则会发生以下情况:

    1. 您正在其构造函数中将MyButton.Content设置为“123”。
    2. 您正在XAML中设置TwoWay绑定。这清除了您设置的值。
    3. 您的控件值(MyButton.Content)使用来源进行更新,在这种情况下,您的viewModels Txt属性会导致您的MyButton.Content属性等于“123”。