简体   繁体   English

自定义UserControl的WPF数据绑定问题

[英]WPF DataBinding Problems with custom UserControl

I am using DataBinding on a DependencyProperty of an UserControl and on a DependencyProperty of a TextBlock. 我在UserControl的DependencyProperty和TextBlock的DependencyProperty上使用DataBinding。 I am binding those to a Property TestValue. 我将那些绑定到属性TestValue。 First I initialize TestValue with TestValue="TestValue set first time!"; 首先,我使用TestValue =“第一次设置TestValue来初始化TestValue!”; That Works! 这样可行! Both, UserControl and TextBlock get updated. UserControl和TextBlock均得到更新。
Then, by a DispatcherTimer, I write a random value to TestValue every second and this Does not Work . 然后,通过DispatcherTimer,我每秒将一个随机值写入TestValue,这不起作用 The TextBlock gets updated, but the UserControl not. TextBlock得到更新,但UserControl没有更新。

Here ist my code of the MainWindow: 这是我的MainWindow代码:

//MainWindow.cs
public partial class MainWindow : Window
{
    private MainWindowViewModel viewModel;

    public MainWindow()
    {
        InitializeComponent();

        this.DataContext = this.viewModel = new MainWindowViewModel()
        {
            TestValue = "TestValue set first time!",
        };
    }
}

//MainWindow.xaml
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local ="clr-namespace:RhinoTouchUIwpf;assembly="
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="RhinoTouchUIwpf.MainWindow"
    SizeToContent="WidthAndHeight">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="auto"/>
    </Grid.ColumnDefinitions>
    <local:UserControl1 OperationName="{Binding TestValue, Mode=OneWay}" Grid.Row="0"/>
    <TextBlock Text="{Binding TestValue, Mode=OneWay}" FontSize="15" Grid.Row="1" />
</Grid>

And here is the code of the UserControl: 这是UserControl的代码:

//UserControl1.cs
public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
    }

    public static DependencyProperty MyDependencyProperty =DependencyProperty.Register("OperationName", typeof(String), typeof(UserControl1), new UIPropertyMetadata(null, MyDependencyPropertyChangedHandler));

    private static void MyDependencyPropertyChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        ((UserControl1)sender).OperationName = (String)e.NewValue;
        ((UserControl1)sender).NumberLabel.Text = (String)e.NewValue;
    }
    public String OperationName
    {
        get { return (string)(this.GetValue(MyDependencyProperty)); }
        set { this.SetValue(MyDependencyProperty, value); }
    }
}

//UserControl1.xaml
<UserControl
         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:RhinoTouchUIwpf" x:Class="RhinoTouchUIwpf.UserControl1" 
         mc:Ignorable="d">

<Border BorderThickness="1" BorderBrush="Blue" x:Name="ControlBorder">
    <Grid Background="AliceBlue">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBlock x:Name="NumberLabel" FontSize="20"  Text="0" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</Border>

This is the code of the MainWindowViewModel, whose property I change by the DispatcherTimer every second. 这是MainWindowViewModel的代码,我的属性由DispatcherTimer每秒更改一次。

public class MainWindowViewModel : ViewModelBase
{
    DispatcherTimer dispatcherTimer; 

    public MainWindowViewModel()
    {
        dispatcherTimer = new DispatcherTimer();
        dispatcherTimer.Tick += Dispatcher_Tick;
        dispatcherTimer.Interval = TimeSpan.FromSeconds(1);
        dispatcherTimer.IsEnabled = true;
    }

    void Dispatcher_Tick(object sender, EventArgs e)
    {
        this.TestValue = "" + DateTime.Now.Millisecond;
    }

    private String _TestValue;

    public String TestValue
    {
        get { return _TestValue; }
        set
        {
            _TestValue = value;
            OnPropertyChanged("TestValue");
        }
    }

    private static readonly MainWindowViewModel NullInstance = null;

}

public abstract class ViewModelBase : System.ComponentModel.INotifyPropertyChanged
{
    #region INotifyPropertyChanged Members

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this,
                new System.ComponentModel.PropertyChangedEventArgs(propertyName));
    }

    #endregion
}

The text of the TextBlock changes every second, the text of the UserControl1 doesn't. TextBlock的文本每秒更改一次,而UserControl1的文本则不更改。
What am I doing wrong? 我究竟做错了什么?

The problem you are having is this line of code in your PropertyChangedCallback : 您遇到的问题是PropertyChangedCallback以下代码行:

((UserControl1)sender).OperationName = (String)e.NewValue;

The problem here is that the event handler will notify you of a new change to that same property. 这里的问题是事件处理程序将通知您对该属性的新更改。 When you enter this function, it is coming from: 当您输入此功能时,它来自:

set { this.SetValue(MyDependencyProperty, value); }

Effectively entering an infinite loop (that the dependency property system detect and stops further call to that function). 有效地进入无限循环(依赖属性系统检测到并停止对该函数的进一步调用)。 Removing that line will solve your problem. 删除该行将解决您的问题。

On the other hand, you can get rid of the PropertyChangedCallback altogether if you define this in your XAML instead: 另一方面,如果您在XAML中定义它,则可以完全摆脱PropertyChangedCallback

// On your UserControl element, add a x:Name="uc"
<TextBlock x:Name="NumberLabel" FontSize="20"  
           Text="{Binding OperationName, ElementName=uc, FallbackValue=0}" 
           Grid.Column="0" Grid.Row="1" HorizontalAlignment="Center" 
           VerticalAlignment="Center"/>

Here's the full UserControl1.xaml 这是完整的UserControl1.xaml

<UserControl x:Class="WpfApplication1.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" x:Name="uc"
             d:DesignHeight="300" d:DesignWidth="300">
    <Border BorderThickness="1" BorderBrush="Blue" x:Name="ControlBorder">
        <Grid Background="AliceBlue">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <TextBlock x:Name="NumberLabel" FontSize="20"  
                        Text="{Binding OperationName, ElementName=uc, FallbackValue=0}" 
                        Grid.Column="0" Grid.Row="1" HorizontalAlignment="Center" 
                        VerticalAlignment="Center"/>
        </Grid>
    </Border>
</UserControl>

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

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