绑定值未传递给 WPF 中的用户控件 [英] Binding value not passed to user control in WPF

查看:31
本文介绍了绑定值未传递给 WPF 中的用户控件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看起来很长很硬,但被卡住了.我正在尝试通过 Window 的绑定将参数从 Window 传递到 UserControl1.

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.

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

In the MainWindow, the UserControl1 is included twice, once passing the parameter MyCustom via a binding on MyValue, again with a literal. Passing with the binding has no effect on UserControl1. MyCustom dependency property is not changed. With the literal, it works as expected.

我很困惑.我已经复制了 https://stackoverflow.com/a/21718694/468523 中的示例,但没有任何乐趣.一定有一些简单的东西我错过了.

I'm very perplexed. I've copied the example in https://stackoverflow.com/a/21718694/468523 but no joy. 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

<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>

推荐答案

在窗口 XAML 中,默认情况下,用户控件实例上的绑定使用用户控件的 DataContext 作为其源.您假设它从窗口继承了它的数据上下文.

In the window XAML, the bindings on the usercontrol instance use the usercontrol's DataContext as their source, by default. You're assuming that it's inheriting its datacontext from the window.

但这是 UserControl 中的:

But here's this in the UserControl:

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

这打破了父级赋予它的所有绑定.所以不要那样做.使用相对来源:

That breaks all the bindings the parent gives it. So don't do that. Use 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 对绑定到永远不会更新其源的属性没有任何作用,因此可以省略.

  1. UpdateSourceTrigger=PropertyChanged doesn't serve any purpose on a binding to a property that never updates its source, so that can be omitted.

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

As we discussed in comments, INotifyPropertyChanged isn't needed for dependency properties.

当绑定不起作用时非常令人沮丧,因为您如何调试它们?你什么也看不见.关键是它在哪里寻找这个属性?你可以得到这样的诊断信息:

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>

这将在运行时向 Visual Studio 的输出窗格发出大量调试信息.它会准确地告诉您 Binding 正在尝试做什么,一步一步地,它找到了什么,以及它在哪里失败.

That will emit a great deal of debugging information to the Output pane of Visual Studio at runtime. It will tell you exactly what the Binding is trying to do, step by step, what it finds, and where it fails.

窗口可以将自己的 DataContext 设置为 Self,因为它没有父级,所以它不会踩到继承的 DataContext.但是,窗口可以并且应该使用 RelativeSource 本身——或者更好的是,编写一个主视图模型类(您已经知道如何实现 INPC),将窗口的属性移动到主视图模型,并将视图模型的实例分配给窗口的数据上下文.

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. 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.

这篇关于绑定值未传递给 WPF 中的用户控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆