简体   繁体   English

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

[英]Binding value not passed to user control in WPF

I've looked long and hard and am stuck. 我看起来很久很努力,被困住了。 I'm trying to pass a parameter from Window to UserControl1 via a binding from Window. 我试图通过Window的绑定将参数从Window传递给UserControl1。

In the MainWindow, the UserControl1 is included twice, once passing the parameter MyCustom via a binding on MyValue, again with a literal. 在MainWindow中,两次包含UserControl1,一次是通过对MyValue的绑定传递参数MyCustom,再次是文本。 Passing with the binding has no effect on UserControl1. 绑定传递对UserControl1无效。 MyCustom dependency property is not changed. MyCustom依赖项属性未更改。 With the literal, it works as expected. 使用文字,它可以按预期工作。

I'm very perplexed. 我很困惑。 I've copied the example in https://stackoverflow.com/a/21718694/468523 but no joy. 我已将示例复制到https://stackoverflow.com/a/21718694/468523,但没有乐趣。 There must be something simple I'm missing. 我一定缺少一些简单的东西。

Sorry about all the code I copied but the devil is often in the details .. 对不起,我复制了所有代码,但细节常常出问题。

MainWindow.xaml 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 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 (corrected below) 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 (corrected below) 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;
        }
    }
}

Corrected UserControl1.xaml (per Ed Plunkett) 更正了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>

Corrected UserControl1.xaml.cs (per Ed Plunkett) 更正了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>

In the window XAML, the bindings on the usercontrol instance use the usercontrol's DataContext as their source, by default. 在XAML窗口中,默认情况下,usercontrol实例上的绑定使用usercontrol的DataContext作为其源。 You're assuming that it's inheriting its datacontext from the window. 您假设它正在从窗口继承其数据上下文。

But here's this in the UserControl: 但是,这是在UserControl中:

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

That breaks all the bindings the parent gives it. 这打破了父母给它的所有绑定。 So don't do that. 所以不要那样做。 Use relativesource: 使用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>

Also: 也:

  1. UpdateSourceTrigger=PropertyChanged doesn't serve any purpose on a binding to a property that never updates its source, so that can be omitted. UpdateSourceTrigger=PropertyChanged对于绑定到永远不会更新其源的属性没有任何作用,因此可以省略。

  2. As we discussed in comments, INotifyPropertyChanged isn't needed for dependency properties. 正如我们在评论中讨论的那样,依赖项属性不需要INotifyPropertyChanged

  3. It's immensely frustrating when bindings just don't work, because how do you debug them? 当绑定不起作用时,这非常令人沮丧,因为您如何调试它们? You can't see anything. 你什么都看不到。 The critical thing is where is it looking for this property? 关键是它在哪里寻找该属性? You can get diagnostic information like this: 您可以获取如下诊断信息:

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

    That will emit a great deal of debugging information to the Output pane of Visual Studio at runtime. 这将在运行时向Visual Studio的“输出”窗格发出大量调试信息。 It will tell you exactly what the Binding is trying to do, step by step, what it finds, and where it fails. 它会告诉您Binding到底要做什么,逐步,找到的内容以及失败的地方。

  4. The window can get away with setting its own DataContext to Self because it has no parent, so it's not stepping on an inherited DataContext. 窗口可以将其自己的DataContext设置为Self,因为它没有父级,因此不会踩到继承的DataContext。 However, the window can and should use RelativeSource itself -- or better yet, write a main viewmodel class (you know how to implement INPC already), move the window's properties to the main viewmodel, and assign an instance of the viewmodel to the window's DataContext. 但是,窗口可以并且应该使用RelativeSource本身,或者更好的方法是,编写一个主视图模型类(您已经知道如何实现INPC),将窗口的属性移至主视图模型,然后将视图模型的实例分配给窗口的DataContext。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM