RelativeSource-Binding for UserControl

南楼画角 提交于 2020-07-30 03:15:07

问题


I've created a UserControl for displaying a Hyperlink in my application.

The markup of this UserControl looks like:

<UserControl x:Class="MVVMExample.View.UserControls.ActionLink"
             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>
        <TextBlock Margin="5">
                <Hyperlink Command="{Binding LinkCommand}" CommandParameter="{Binding LinkCommandParameter}">
                    <TextBlock Text="{Binding LinkText, UpdateSourceTrigger=PropertyChanged}"/>
                </Hyperlink>
        </TextBlock>
    </Grid>
</UserControl>

The DataContext of this UserControl is in the CodeBehind which looks like:

public partial class ActionLink : UserControl, INotifyPropertyChanged
{
    public static readonly DependencyProperty LinkTextProperty = DependencyProperty.Register(
        "LinkText", typeof (string), typeof (ActionLink), new PropertyMetadata(LinkTextChanged));

    public static readonly DependencyProperty LinkCommandParameterProperty = DependencyProperty.Register(
        "LinkCommandParameter", typeof (object), typeof (ActionLink),
        new PropertyMetadata(LinkCommandParameterChanged));

    public static readonly DependencyProperty LinkCommandProperty = DependencyProperty.Register(
        "LinkCommand", typeof (ICommand), typeof (ActionLink), new PropertyMetadata(LinkCommandChanged));

    public ActionLink()
    {
        InitializeComponent();
    }

    public object LinkCommandParameter
    {
        get { return GetValue(LinkCommandParameterProperty); }
        set { SetValue(LinkCommandParameterProperty, value); }
    }

    public string LinkText
    {
        get { return (string) GetValue(LinkTextProperty); }
        set
        {
            SetValue(LinkTextProperty, value);
            OnPropertyChanged();
        }
    }

    public ICommand LinkCommand
    {
        get { return (ICommand) GetValue(LinkCommandProperty); }
        set { SetValue(LinkCommandProperty, value); }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private static void LinkTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((ActionLink) d).LinkText = (string) e.NewValue;
    }

    private static void LinkCommandParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((ActionLink) d).LinkCommandParameter = e.NewValue;
    }

    private static void LinkCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((ActionLink) d).LinkCommand = (ICommand) e.NewValue;
    }

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Everything just works fine.

Now if I want to use this UserControl with the Command-Binding I have to do the following:

<userControls:ActionLink LinkText="View customers" LinkCommand="{Binding DataContext.ViewCustomersCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"/>

If I use a Button instead I don't have to provide that RelativeSource. Is there a opportunity that I also don't have to provide a RelativeSource on binding properties of custom created UserControls?


回答1:


When you write

<userControls:ActionLink LinkCommand="{Binding ViewCustomersCommand}"/>

WPF tries to establish a data binding to a ViewCustomersCommand property in the DataContext of your UserControl, which is usually inherited from the control's parent, and holds a reference to some view model object. This doesn't work here because you have explicitly set the DataContext to the UserControl instance.

As soon as you have bindable properties (i.e. dependency properties) in your UserControl, you should not set its DataContext. If you do so, you will always have to specifiy the binding source objects explicitly, because the DataContext is no longer inherited.

So remove the

DataContext="{Binding RelativeSource={RelativeSource Self}}"

setting from your UserControl's XAML and set a RelativeSource in all its internal bindings:

<Hyperlink
    Command="{Binding LinkCommand,
              RelativeSource={RelativeSource AncestorType=UserControl}}"
    CommandParameter="{Binding LinkCommandParameter,
                       RelativeSource={RelativeSource AncestorType=UserControl}}">
    <TextBlock
        Text="{Binding LinkText,
               RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</Hyperlink>


来源:https://stackoverflow.com/questions/29075720/relativesource-binding-for-usercontrol

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!