【问题标题】:WPF DataBinding Problems with custom UserControl自定义用户控件的 WPF 数据绑定问题
【发布时间】:2013-08-06 15:15:03
【问题描述】:

我在 UserControl 的 DependencyProperty 和 TextBlock 的 DependencyProperty 上使用 DataBinding。我将它们绑定到属性 TestValue。 首先我用 TestValue="TestValue set first time!" 初始化 TestValue 有效! UserControl 和 TextBlock 都已更新。
然后,通过 DispatcherTimer,我每秒向 TestValue 写入一个随机值,这 不起作用。 TextBlock 得到更新,但 UserControl 没有。

这是我的 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>

这里是用户控件的代码:

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

这是 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
}

TextBlock 的文本每秒都在变化,而 UserControl1 的文本则不会。
我做错了什么?

【问题讨论】:

    标签: c# wpf data-binding


    【解决方案1】:

    您遇到的问题是PropertyChangedCallback 中的这行代码:

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

    这里的问题是事件处理程序会通知您对同一属性的新更改。当你进入这个函数时,它来自:

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

    有效地进入一个无限循环(依赖属性系统检测并停止对该函数的进一步调用)。删除该行将解决您的问题。

    另一方面,如果您在 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"/>
    

    这是完整的 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>
    

    【讨论】:

      猜你喜欢
      • 2015-07-07
      • 1970-01-01
      • 1970-01-01
      • 2010-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多