WPF链绑定

时间:2014-05-05 22:09:23

标签: c# wpf xaml user-controls

我想像这样进行链式绑定:我在一个具有类似dependencyproperty的窗口中有一个带有dependencyproperty的usercontrol。我想将usercontrol的dependencyproperty绑定到窗口的dependencyproperty。

我创建了一个示例项目来演示我的问题:

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"
             d:DesignHeight="300" d:DesignWidth="300" DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
        <Label Content="{Binding Caption}"/>
    </Grid>
</UserControl>

UserControl1 C#:

public partial class UserControl1 : UserControl
  {
    public static readonly DependencyProperty CaptionProperty = DependencyProperty.Register("Caption", typeof(string), typeof(UserControl1));
    public string Caption
    {
      get { return (string)GetValue(CaptionProperty); }
      set { SetValue(CaptionProperty, value); }
    }

    public UserControl1()
    {
      InitializeComponent();
    }
  }

MainWindow XAML:

<Window xmlns:WpfApplication1="clr-namespace:WpfApplication1"  x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Label Content="{Binding Caption, Mode=OneWay}"/>
        <WpfApplication1:UserControl1 x:Name="uc" Caption="{Binding Caption, Mode=OneWay}"  Grid.Row="1"/>
    </Grid>
</Window>

MainWindow C#:

public partial class MainWindow : Window
  {
    public static readonly DependencyProperty CaptionProperty = DependencyProperty.Register("Caption", typeof(string), typeof(MainWindow));
    public string Caption
    {
      get { return (string)GetValue(CaptionProperty); }
      set { SetValue(CaptionProperty, value); }
    }

    public MainWindow()
    {
      InitializeComponent();
      (new Thread(() => { Thread.Sleep(2000); Dispatcher.Invoke(() => { uc.Caption = "YYY"; Caption = "XXX"; }); })).Start();
    }
  }

当我将标题设置为&#34; XXX&#34; (窗口)我希望它也通知用户控件并更新其标题,但它没有。我想避免附加的依赖属性,尽可能避免代码落后。有什么想法吗?

感谢您的任何努力。

2 个答案:

答案 0 :(得分:3)

问题出在你的绑定上。默认情况下,绑定会在控件的DataContext属性中查找属性。每个绑定都有一个源,在您的情况下它是控件的DataContext属性。所以你的绑定正在评估DataContext.Caption。你真正想要的是制作绑定源,即具有Caption属性的窗口。因此,请按照以下说明更改代码。我在我的机器上测试它,它的工作原理。请记住初始化窗口的Caption属性。

新:

<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" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <Grid>
        <Label Content="{Binding Path=Caption, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl}}"/>
    </Grid>
</Grid>

新:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:current="clr-namespace:WpfApplication1"
    Title="MainWindow" Height="350" Width="525" >
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <Label Content="{Binding Caption, Mode=OneWay, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"/>
    <current:UserControl1 x:Name="uc" Caption="{Binding Path=Caption, Mode=OneWay, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"  Grid.Row="1"/>
</Grid>

答案 1 :(得分:0)

您可以按照......

的方式创建用户控件
<UserControl x:Class="ChainBinding.CaptionGuy"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             >
    <Grid>
            <Label Name="CaptionLabel"/>
    </Grid>
</UserControl>

...并使用您的依赖属性来检测它......

    #region Caption (DependencyProperty)
    public string Caption
    {
        get { return (string)GetValue(CaptionProperty); }
        set { SetValue(CaptionProperty, value); }
    }
    public static readonly DependencyProperty CaptionProperty =
        DependencyProperty.Register("Caption", typeof(string), typeof(CaptionGuy),
          new PropertyMetadata{PropertyChangedCallback = CaptionChanged});
    private static void CaptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        CaptionGuy cg = d as CaptionGuy;
        if (cg != null && e.NewValue!=null)
        {
            cg.CaptionLabel.Content = e.NewValue.ToString();
        }
    }
    #endregion

然后你可以在像这样的WPF应用程序中部署它......

<Grid>
    <chainBinding:CaptionGuy Caption="{Binding VmCaption}"/>
</Grid>

相应的视图模型(或代码隐藏,如果这是你的设计)看起来像这样......

public class ViewModel : INotifyPropertyChanged
{
    public ViewModel()
    {
        Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
        DispatcherTimer dt = new DispatcherTimer(new TimeSpan(0,0,0,5), DispatcherPriority.Normal,
            delegate
            {
                VmCaption = DateTime.Now.ToString("G");
            }, dispatcher);
        dt.Start();
    }
    private string _vmCaption;
    public string VmCaption
    {
        [DebuggerStepThrough]
        get { return _vmCaption; }
        [DebuggerStepThrough]
        set
        {
            if (value != _vmCaption)
            {
                _vmCaption = value;
                OnPropertyChanged("VmCaption");
            }
        }
    }
    #region INotifyPropertyChanged Implementation
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string name)
    {
        var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
    #endregion
}

此示例仅使用每5秒的时间更新标题。

当然这个答案在依赖属性上使用了一个回调,但这是一个在声明性编程方面胜过间接实用性的问题。