【问题标题】:Dependency Property UserControl Binding not working依赖属性用户控件绑定不起作用
【发布时间】:2015-05-04 02:37:17
【问题描述】:

我的项目是在 MVVM 中实现的。我有一个 MainWindow,它由一个状态栏和一个 tabview 组成。在 tabview 内部,有一个名为“AnnotationView”的 UserControl。 Annotationview 是两个较小的用户控件的父级,称为 TimePicker。 TimePicker 由两个文本框组成,一个用于小时,一个用于分钟。我想使用这个 UserControl 两次(这也是为什么我将它设为自己的 Control,以便以后重用它)。

TimePicker 的 XAML:

<UserControl x:Class="archidb.Views.TimePicker"
             x:Name="TimePickerControl"
             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" Height="Auto" Width="Auto"
             KeyboardNavigation.TabNavigation="Local">

    <Grid DataContext="{Binding ElementName=TimePickerControl}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition Width="5"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <TextBox Style="{StaticResource TextBoxStyle}"
                 Text="{Binding Path=HourValue}"
                 x:Name="tbHours"
                 KeyboardNavigation.TabIndex="0"/>
        <TextBlock Style="{StaticResource TextBlockStyle}"
                   Margin="0 -3 0 5"
                   Text=":"
                   Grid.Column="1"/>
        <TextBox Grid.Column="2" Style="{StaticResource TextBoxStyle}"
                 Text="{Binding Path=MinuteValue}"
                 x:Name="tbMinutes"
                 KeyboardNavigation.TabIndex="1"/>
    </Grid>
</UserControl>

TimePicker 代码隐藏:

public partial class TimePicker : UserControl
{
    public static readonly DependencyProperty HourValueProperty = DependencyProperty.Register("HourValue", typeof(string), typeof(TimePicker), new PropertyMetadata("00"));
    public string HourValue
    {
        get { return (string)GetValue(HourValueProperty); }
        set { SetValue(HourValueProperty, value); }
    }

    public static readonly DependencyProperty MinuteValueProperty = DependencyProperty.Register("MinuteValue", typeof(string), typeof(TimePicker), new PropertyMetadata("00"));
    public string MinuteValue
    {
        get { return (string)GetValue(MinuteValueProperty); }
        set { SetValue(MinuteValueProperty, value); }
    }

    public TimePicker()
    {
        InitializeComponent();
    }
}

在 AnnotationControl 中,我像这样插入 UserControl:

<v:TimePicker x:Name="tpStart"
              HourValue="{Binding Path=StartHours}"
              MinuteValue="{Binding Path=StartMinutes}"
              KeyboardNavigation.TabIndex="2"/>

<v:TimePicker x:Name="tpEnde"
              HourValue="{Binding Path=EndHours}"
              MinuteValue="{Binding Path=EndMinutes}"
              KeyboardNavigation.TabIndex="3"
              Grid.Row="2"/>

AnnotationControl 的 DataContext 设置为其视图模型,我在其中声明了属性。

问题是,绑定不起作用。我在依赖属性(“00”)中设置的默认值没有显示在任何文本框中。此外,如果我在文本框中写了一些东西,AnnotationControl 的视图模型中的属性不会改变它的值。这个问题已经困扰我好几天了,我到底做错了什么?

【问题讨论】:

    标签: c# wpf xaml mvvm user-controls


    【解决方案1】:

    解决方案是在您的UserControl 中简单地使用RelativeSource Binding不将其设置为DataContext 自身

    在您的控制之下:

    <TextBox Style="{StaticResource TextBoxStyle}" Text="{Binding HourValue, RelativeSource=
        {RelativeSource AncestorType={x:Type YourPrefixToBeAdded:TimePickerControl}}}"
        x:Name="tbHours" KeyboardNavigation.TabIndex="0" />
    <TextBlock Style="{StaticResource TextBlockStyle}" Margin="0 -3 0 5" Text=":" 
        Grid.Column="1" />
    <TextBox Grid.Column="2" Style="{StaticResource TextBoxStyle}" Text="{Binding 
        MinuteValue, RelativeSource={RelativeSource AncestorType={x:Type 
        YourPrefixToBeAdded:TimePickerControl}}}" x:Name="tbMinutes" 
        KeyboardNavigation.TabIndex="1" />
    

    然后您将能够从控件外部对属性进行数据绑定。

    【讨论】:

    • 感谢您的回复。可悲的是,它仍然不起作用。我更改了 TimePicker 中的绑定并从网格中删除了 DataContext 设置。 AnnotationControl 的 viewmodel 的属性在调试时仍然显示 null 值。我是否还需要更改 AnnotationControl XAML 中的绑定?
    • 如果您需要将更改反映在控件之外,您可以添加Mode=TwoWay,但这不应该阻止它工作。在 Visual Studio 的输出窗口中出现什么错误?
    • Mode=TwoWay 添加到 AnnotationControl.xaml 中的 Binding 就可以了。谢谢!
    【解决方案2】:

    最简单的方法: 删除该行:

    DataContext="{Binding ElementName=TimePickerControl}"
    

    像这样绑定:

    "{Binding ElementName=TimePickerControl, Path=HourValue}"
    

    另一种方式:(更清洁)

    在 TimePicker 构造函数中设置:

      (this.Content as FrameworkElement).DataContext = this;
    

    像这样绑定:

    "{Binding HourValue}"
    

    经过阅读,您的示例应该可以正常工作,因此我假设您没有为您的 ViewModel 实现 INotifyPropertyChanged,“00”未显示,因为绑定有效,属性设置为默认值并且它们的值未更新。

    【讨论】:

    • 那也不行。我确实在 BaseViewModel 类中实现了 INotifyPropertyChanged,但 TimePicker 的视图模型当前为空。
    • DataContext = this; 是您可以给任何尝试正确使用数据绑定的人提供的最糟糕的建议。请不要。任何人读到这篇文章,你都会因为这个建议而给自己带来痛苦和痛苦。不要这样做。
    • 威尔,我想是你给了我减号,但你错了。我没有设置 UserControl 的 DataContext。我设置了 Content.DataContext,这意味着我实际上设置了第一个 UserControl 的孩子的 DataContext。它不会破坏数据绑定,一切正常。
    【解决方案3】:

    首先,在 UserControl 构造函数中,将 DataContext 设置为自身:

    public TimePicker()
    {
       InitializeComponent();
    
       this.DataContext = this;
    }
    

    另外,从用户控件的网格中删除它:

    DataContext="{Binding ElementName=TimePickerControl}"
    

    你的用户控件的DataContext已经在用户控件中设置好了,所以当你使用下面的代码时:

    HourValue="{Binding Path=EndHours}"
    

    您实际上是在询问“绑定到 UserControl 的 DataContext 中的 EndHours”。

    您需要更改绑定,以便它查看父窗口的 DataContext。为父窗口命名并使用以下内容:

    HourValue="{Binding DataContext.EndHours, ElementName=windowName}"
    

    【讨论】:

    • 另外,我想说的是,在 UserControl DataContext 中不应该改变。
    • 您的意思是“第一个”UserControl AnnotationView 吗?如果是,我已经尝试过您的建议,但遗憾的是没有奏效。
    • @Zbigniew 如果我从 TimePicker 中删除 DataContext,我会收到绑定表达式路径错误。
    • @refl3x 您是否将 UserControl 的 DataContext 设置为自身?使用: this.DataContext = this;还是 XAML 变体?
    • 名为 AnnotationView 的控件的 DataContext 设置为它的视图模型。 Timepicker 的 DataContext 未设置,但 Timepicker 中包含的网格的 DataContext 设置为 TimePicker 元素,如问题所示。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-24
    • 1970-01-01
    相关资源
    最近更新 更多