【问题标题】:Binding value not passed to user control in WPF绑定值未传递给 WPF 中的用户控件
【发布时间】:2018-01-04 16:11:02
【问题描述】:

我看了又长又硬,卡住了。我正在尝试通过 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>

【问题讨论】:

  • 你不需要 INPC 的东西。依赖属性不使用它,用户控件应该使用依赖属性。您的问题是您通过踩到 DataContext 破坏了用户控件上的所有绑定。不要那样做。在 UC XAML 中使用相对源绑定。您所遵循的示例并没有告诉您破坏 DataContext。
  • 你是对的,在这种情况下是不必要的。但已删除,仍然无法正常工作。
  • 所以,我还删除了用户控件中的 DataContext 设置,但随后用户控件 xaml 绑定到 MyCustom 中断了。
  • 如上:“在 UC XAML 中使用相对源绑定”。详情见答案。

标签: c# wpf xaml


【解决方案1】:

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

但这是在 UserControl 中的:

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

这会破坏父级赋予它的所有绑定。所以不要那样做。使用相对源:

<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. 正如我们在 cmets 中所讨论的,依赖属性不需要 INotifyPropertyChanged

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

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

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

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

【讨论】:

  • 可爱的答案,谢谢。我想知道调试绑定,所以特别感谢这些信息。我对用户控件进行了更改,现在一切都很开心。我会发布更新的代码。具有讽刺意味的是,我曾认为 拥有一个单独的视图模型类(就像我通常那样)会使事情变得更简单。傻我。
  • 那么,如何获得用户控件的视图模型?我环顾四周,看到一些似乎阴暗的答案。还是我应该问一个单独的问题?
猜你喜欢
  • 1970-01-01
  • 2012-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多