绑定导致StackOverflow

时间:2018-02-09 13:47:28

标签: c# wpf xaml binding

我不确定我在这里做错了什么。

让我们说,我有两个UserControl BoxABoxB。两者都有一个名为Text

的DependencyProperty

BoxB包装BoxA,它有一个常规的TextBox。

绑定应该像这个BoxB.Text< =>一样工作BoxA.Text< => TextBox.Text

Xaml BoxA:

<UserControl x:Class="SandBoxWpf.BoxA"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             DataContext="{Binding RelativeSource={RelativeSource Self}}">

    <TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></TextBox>

</UserControl>

Xaml BoxB:

<UserControl x:Class="SandBoxWpf.BoxB"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:SandBoxWpf"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             DataContext="{Binding RelativeSource={RelativeSource Self}}">

    <local:BoxA Text="{Binding Text, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></local:BoxA>

</UserControl>

BoxA和BoxB的代码隐藏

using System.Windows;
using System.Windows.Controls;

namespace SandBoxWpf
{
    /// <summary>
    /// Interaktionslogik für BoxA.xaml
    /// </summary>
    public partial class BoxX : UserControl
    {
        public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
                "Text",
                typeof(string),
                typeof(BoxX),
                new PropertyMetadata(default(string)));

        public string Text
        {
            get => (string) GetValue(TextProperty);
            set => SetValue(TextProperty, value);
        }

        public BoxX()
        {
            InitializeComponent();
        }
    }
}

主窗口

<Window x:Class="SandBoxWpf.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SandBoxWpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
        <local:BoxB Width="100" Height="20" Text="{Binding Title}"></local:BoxB>
    </Grid>
</Window>

只要我在BoxB中输入内容,我就会收到StackoverflowException。 如果我删除了Mode = TwoWay或UpdateSourceTrigger,StackOverflow就消失了,但绑定也不起作用。

2 个答案:

答案 0 :(得分:4)

如果要构建具有可绑定属性的UserControl(即依赖项属性),则必须在任何情况下都无法显式设置UserControl的DataContext ,无论是控件实例还是任何私有视图模型。 / p>

如果你这样做,就像

这样的绑定
<local:BoxB Text="{Binding Title}">

将不再有效。 Binding期望当前DataContext中对象的Title属性。 DataContext属性值通常从UserControl的父元素继承,例如,窗户。但是,由于您已明确设置DataContext,因此可以避免使用此机制。

这对于UserControls中同名的属性尤其令人困惑。当你写

<local:BoxA Text="{Binding Text, ...}"/>
在UserControl BoxB中

,您的期望是绑定源属性为BoxB.Text。实际上它是BoxA.Text,因为BoxA的DataContext是BoxA实例。

所以删除任何

DataContext="{Binding RelativeSource={RelativeSource Self}}"

行并使用RelativeSource在UserControl的XAML中编写Bindings,如下所示:

<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay},
                RelativeSource={RelativeSource AncestorType=UserControl}"/>

<local:BoxA Text="{Binding Text, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay},
                   RelativeSource={RelativeSource AncestorType=UserControl}"/>

答案 1 :(得分:-1)

对于任何形式的变更通知,一个危险就是我称之为“乒乓”的问题。例如:

  1. 属性A更改。
  2. 属性B更改为匹配A.
  3. 物业B改变了。
  4. 属性A更改为匹配B.
  5. 递归1
  6. 为了避免这种情况,Properties with Change notificaiton的示例代码如下所示:

    public string PhoneNumber
    {
        get
        {
            return this.phoneNumberValue;
        }
    
        set
        {
            if (value != this.phoneNumberValue)
            {
                this.phoneNumberValue = value;
                NotifyPropertyChanged();
            }
        }
    }
    

    如果输入与输出相同,则不执行任何操作。结果如下:

    1. 属性A更改
    2. 物业B被拣选以匹配A
    3. Proeprty B改变
    4. 属性A注意到它已经具有该值,所以什么也没做。
    5. 我最好的猜测是WPF Elements没有这样的保护。其中一个案例是“试图变得聪明可能会导致真正的愚蠢”。