绑定值未传递给WPF中的用户控件

时间:2018-01-04 16:11:02

标签: c# wpf xaml

我看起来很长很难,而且被困住了。我试图通过Window的绑定将参数从Window传递给UserControl1。

在MainWindow中,UserControl1包含两次,一次通过MyValue上的绑定传递参数MyCustom,再次使用文字。使用绑定传递对UserControl1没有影响。 MyCustom依赖项属性未更改。使用文字,它按预期工作。

我很困惑。我在https://stackoverflow.com/a/21718694/468523复制了这个例子但没有快乐。必须有一些我想念的简单。

对于我复制的所有代码感到很抱歉,但是恶魔通常都在详细信息中。

MainWindow.xaml

<Window x:Class="MyParamaterizedTest3.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:MyParamaterizedTest3"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
        <StackPanel>
            <Rectangle Height="20"/>
            <local:UserControl1 MyCustom="{Binding MyValue, UpdateSourceTrigger=PropertyChanged}"/>
            <Rectangle Height="20"/>
            <local:UserControl1 MyCustom="Literal Stuff"/>
            <Rectangle Height="20"/>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="MainWindow: "/>
                <TextBlock Text="{Binding MyValue, UpdateSourceTrigger=PropertyChanged}"/>
            </StackPanel>
        </StackPanel>
    </Grid>
</Window>

MainWindow.xaml.cs

namespace MyParamaterizedTest3
{
    public partial class MainWindow : INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        public string MyValue { get => _myValue; set => SetField(ref _myValue, value); }
        private string _myValue= "First things first";
        public event PropertyChangedEventHandler PropertyChanged;
        protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(field, value)) { return false; }
            field = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            return true;
        }
    }
}

UserControl1.xaml(已在下方更正)

<UserControl x:Class="MyParamaterizedTest3.UserControl1"
             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:MyParamaterizedTest3"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             DataContext="{Binding RelativeSource={RelativeSource Self}}"
             >
    <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
          <Border BorderThickness="3" BorderBrush="Black">
              <StackPanel>
                  <TextBlock Text="{Binding MyCustom, UpdateSourceTrigger=PropertyChanged, FallbackValue=mycustom}"></TextBlock>
              </StackPanel>
          </Border>  
    </Grid>
</UserControl>

UserControl1.xaml.cs(已在下方更正)

namespace MyParamaterizedTest3
{
    public partial class UserControl1 : INotifyPropertyChanged
    {
        public UserControl1()
        {
            InitializeComponent();
        }
        public static readonly DependencyProperty MyCustomProperty =
            DependencyProperty.Register("MyCustom", typeof(string), typeof(UserControl1));
        public string MyCustom
        {
            get
            {
                return this.GetValue(MyCustomProperty) as string;
            }
            set
            {
                this.SetValue(MyCustomProperty, value);
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(field, value)) { return false; }
            field = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            return true;
        }
    }
}

更正了UserControl1.xaml(每个Ed Plunkett)

<UserControl x:Class="MyParamaterizedTest3.UserControl1"
             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"
             >
    <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
          <Border BorderThickness="3" BorderBrush="Black">
              <StackPanel>
                <TextBlock Text="{Binding MyCustom, RelativeSource={RelativeSource AncestorType=UserControl}, FallbackValue=mycustom}"></TextBlock>
              </StackPanel>
          </Border>  
    </Grid>
</UserControl>

更正了UserControl1.xaml.cs(每个Ed Plunkett)

<UserControl x:Class="MyParamaterizedTest3.UserControl1"
             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"
             >
    <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
          <Border BorderThickness="3" BorderBrush="Black">
              <StackPanel>
                <TextBlock Text="{Binding MyCustom, RelativeSource={RelativeSource AncestorType=UserControl}, FallbackValue=mycustom}"></TextBlock>
              </StackPanel>
          </Border>  
    </Grid>
</UserControl>

1 个答案:

答案 0 :(得分:3)

在XAML窗口中,默认情况下,usercontrol实例上的绑定使用usercontrol的DataContext作为其源。你假设它从窗口继承了它的datacontext。

但是在UserControl中有这个:

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

这会破坏父级给出的所有绑定。所以不要这样做。使用relativesource:

<UserControl x:Class="MyParamaterizedTest3.UserControl1"
             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:MyParamaterizedTest3"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             >
    <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
          <Border BorderThickness="3" BorderBrush="Black">
              <StackPanel>
                  <TextBlock Text="{Binding MyCustom, RelativeSource={RelativeSource AncestorType=UserControl}, FallbackValue=mycustom}"></TextBlock>
              </StackPanel>
          </Border>  
    </Grid>
</UserControl>

另外:

  1. UpdateSourceTrigger=PropertyChanged对于从不更新其来源的属性的绑定没有任何作用,因此可以省略。

  2. 正如我们在评论中所讨论的,依赖属性不需要INotifyPropertyChanged

  3. 当绑定无法正常工作时,这是非常令人沮丧的,因为你如何调试它们?你看不到任何东西。关键是在哪里寻找这个属性?你可以得到这样的诊断信息:

    <TextBlock 
        Text="{Binding MyCustom, PresentationTraceSources.TraceLevel=High, FallbackValue=mycustom}"></TextBlock>
    

    这将在运行时向Visual Studio的“输出”窗格发出大量调试信息。它会告诉你Binding正在尝试做什么,一步一步,它找到了什么,以及它失败的地方。

  4. 窗口可以通过将自己的DataContext设置为Self来逃脱,因为它没有父级,因此它不会踩到继承的DataContext。但是,窗口可以并且应该使用RelativeSource本身 - 或者更好的是,编写一个主viewmodel类(你知道如何实现INPC),将窗口的属性移动到主视图模型,并分配一个实例viewmodel到窗口的DataContext。