3

我正在使用OneWayToSource绑定,它似乎总是将我的源属性设置为空。为什么呢?这给我带来了麻烦,因为我需要源属性中目标属性的值而不是空值。

这是我的代码:

MyViewModel.cs:

public class MyViewModel
{
    private string str;

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

主窗口.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>

我的按钮.cs:

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

目标属性是MyButton.Content。源属性是MyViewModel.Txt。该Txt属性应设置为“765”,但它为空。

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

编辑:

请查看MyButton构造函数内部。实际上,如果您使用简单TwoWay,它将起作用。我对其进行了测试,它与在构造函数中设置的内容无关。我猜它是有OneWayToSource约束力的。

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

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

4

3 回答 3

10

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

正如我在回答您的其他问题时向您指出的那样,WPF 按以下顺序运行代码:

  • 正常 - 构造函数在这里运行
  • 数据绑定
  • 使成为
  • 已加载

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

但是,在 的 XAML 中Button,您指定了不同的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您的按钮事件中,您会看到您的源属性在设置按钮时正确更新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"变为null

于 2013-04-29T19:05:51.823 回答
4

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

于 2013-04-29T14:04:12.737 回答
2

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

我无法复制这个。鉴于您提供的代码,如果您使用TwoWay绑定模式,则 viewmodelsTxt属性将等于“123”,即在MainWindows构造函数中给出的值。

我对其进行了测试,它与在构造函数中设置的内容无关

正如 Rachel 所指出的,XAML 绑定清除了本地值。绑定发生您的按钮构造函数代码运行之后。然后绑定从依赖属性的元数据中检索它的默认值。这很容易通过在更合适的时间设置值来解决,在绑定建立之后,例如在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等于该值。

如果您MyButton.Content在按钮加载事件中设置属性,这是在上述事件发生之后,因此您的属性设置为您想要的值。

MyViewModel.Txt您可以通过在属性 getter中放置断点来自己验证这一点。该值将按该顺序设置为“123”、null 和“756”。

使用双向绑定:

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

  1. MyButton.Content在其构造函数中设置为“123”。
  2. 您正在 XAML 中设置TwoWay绑定。这将清除您设置的值。
  3. 您的控件值 ( MyButton.Content) 使用Source更新,在本例中是您的 viewModelsTxt属性,导致您的MyButton.Content属性等于“123”。
于 2013-04-29T18:40:24.207 回答