从页面绑定到窗口

本文关键字:窗口 绑定 | 更新日期: 2023-09-27 18:30:06

在WPF中,我有一个窗口和一个框架,其中将显示一些页面。我想从页面绑定到Window(其属性和/或DataContext)。

这是我尝试的一个例子:

<TextBox Text="{Binding Path=Title,
    RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" />

这在窗口的XAML中成功,但在页面的XAML中失败。我如何让它在页面中工作?

从页面绑定到窗口

在这种情况下,我们似乎没有办法成功设置绑定。ElementNameRelativeSourceSource的所有使用都没有帮助。我们知道主窗口可以像这样访问Application.Current.MainWindow。但是,只有当主窗口是应用程序中唯一的窗口时,才能使用以下方法。否则就不安全了。我认为最好的解决方案是实现您自己的附加属性,帮助将页面(或任何FrameworkElement)的DataContext设置为Type指定的某个祖先。这意味着我们将几乎桥接DataContext流,就好像没有任何截断一样。以下是详细的实现:

//the main class used in your XAML code
public static class DataContextService {
    public static readonly DependencyProperty DataContextFromAncestorProperty = DependencyProperty.RegisterAttached("DataContextFromAncestor", typeof(object), typeof(DataContextService), new UIPropertyMetadata(dataContextPropertyChanged));
    public static object GetDataContextFromAncestor(DependencyObject o)
    {
        return o.GetValue(DataContextFromAncestorProperty);
    }
    public static void SetDataContextFromAncestor(DependencyObject o, object value)
    {
        o.SetValue(DataContextFromAncestorProperty, value);
    }
    private static void dataContextPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        var elem = target as FrameworkElement;
        var type = e.NewValue as Type;
        if (type == null || elem == null) return;            
        if (elem.IsLoaded) SetDataContextFromAncestorOfType(elem, type);
        else {                
            elem.Loaded += loadedHandler;
        }                    
    }
    private static void SetDataContextFromAncestorOfType(FrameworkElement elem, Type ancestorType)
    {
        elem.DataContext = elem.FindAncestorOfType(ancestorType);
    }
    private static void loadedHandler(object sender, EventArgs e)
    {
        var elem = sender as FrameworkElement;
        SetDataContextFromAncestorOfType(elem, GetDataContextFromAncestor(elem) as Type);
        elem.Loaded -= loadedHandler;
    }
}
//a helper class to find the first ancestor of some Type
public static class ElementExtension
{
    public static DependencyObject FindAncestorOfType(this DependencyObject o, Type ancestorType)
    {            
        var parent = VisualTreeHelper.GetParent(o);
        if (parent != null)
        {
            if (parent.GetType().IsSubclassOf(ancestorType) || parent.GetType() == ancestorType)
            {
                return parent;
            }
            return FindAncestorOfType(parent, ancestorType);
        }
        return null;
    }
}

在XAML中的用法

//suppose this TextBox is inside your Page
<TextBox Text="{Binding Title}" 
         local:DataContextService.DataContextFromAncestor="{x:Type Window}"/>

请使用{x:Type}指定类型,不要使用简单字符串(例如应该使用{x:Type Window}而不仅仅是Window)。上面实现的类不支持这种简写转换。