How to bind multiple properties in a UserControl

Let's say we have a UserControl:

<UserControl x:Class="...>
    <StackPanel>
        <TextBlock Name="TextBlock1" />
        <TextBlock Name="TextBlock2" />
        <TextBlock Name="TextBlock3" />
        ...
        <TextBlock Name="TextBlock10" />
    </StackPanel>
</UserControl>

      

And we have properties defined like this:

public string Text1 { get; set; }
public string Text2 { get; set; }
public string Text3 { get; set; }
...
public string Text10 { get; set; }

      

And I know that I want to bind all these TextBlocks to all of these properties. Obviously there are several ways to do this, and I am wondering about the (dis-) benefits of different ones. Let's make a list:

  • My first approach:

    <TextBlock Name="TextBlock1" Text="{Binding Path=Text1, RelativeSource={RelativeSource AncestorType=UserControl}}" />
    
          

    It works and is quite simple, but a lot of redundant code if I need to type it for all text blocks. In this example, we could just copy-paste, but the UserControl might be more complex.

  • When I was looking for the problem I found this solution:

    <UserControl ...
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
        <TextBlock Name="TextBlock1" Text="{Binding Path=Text1}" />
    
          

    This looks pretty clean in xaml, however the DataContext can be used differently. So if someone uses this UserControl and changes the DataContext, we are screwed.

  • Another common solution seems to be the following:

    <UserControl ...
        x:Name="MyUserControl">
        <TextBlock Name="TextBlock1" Text="{Binding Path=Text1, ElementName=MyUserControl}" />
    
          

    This has the same problem as 2. although the name can be set from somewhere else.

  • What if we write our own MarkupExtension?

    public class UserControlBindingExtension : MarkupExtension
    {
        public UserControlBindingExtension() { }
    
        public UserControlBindingExtension(string path)
        {
            this.Path = path;
        }
    
        private Binding binding = null;
    
        private string path;
    
        [ConstructorArgument("path")] 
        public string Path
        {
            get
            {
                return path;
            }
            set
            {
                this.path = value;
                binding = new Binding(this.path);
                binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(UserControl), 1);
            }
        }
    
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if(binding == null)
                return null;
    
            return binding.ProvideValue(serviceProvider);
        }
    }
    
          

    Now we can do something like this:

    <UserControl ...
        xmlns:self="clr-namespace:MyProject">
        <TextBlock Name="TextBlock1" Text="{self:UserControlBinding Path=Text1}"
    
          

    Well appointed! But I'm not sure if my implementation is bulletproof and I'd rather not write my own MarkupExtension.

  • Likewise 4. we could do this:

    public class UserControlBindingHelper : MarkupExtension
    {
        public UserControlBindingHelper() { }
    
        public UserControlBindingHelper(Binding binding)
        {
            this.Binding = binding;
        }
    
        private Binding binding;
        [ConstructorArgument("binding")]
        public Binding Binding
        {
            get
            {
                return binding;
            }
            set
            {
                binding = value;
                binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(UserControl), 1);
            }
        }
    
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (binding == null)
                return null;
    
            return binding.ProvideValue(serviceProvider);
        }
    }
    
          

    This will lead to the following code:

    <TextBlock Name="TextBlock1" Text="{self:UserControlBindingHelper {Binding Text1}}" />
    
          

  • We could do it in code!

    private void setBindingToUserControl(FrameworkElement element, DependencyProperty dp, string path)
    {
        Binding binding = new Binding(path);
        binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(StateBar), 1);
        element.SetBinding(dp, binding);
    }
    
          

    And then we do this:

    setBindingToUserControl(this.TextBlock1, TextBlock.Text, "Text1");
    
          

    Pretty nice, but it makes the xaml easier to read as there is no bind information now.

  • What other good / interesting options are there?

So what is the way to go in this situation? Am I wrong somewhere?
To ask the question:
Are all of these methods correct? Are some superior to others?

+3


source to share


2 answers


Looks like you've done a lot of good tests here! As you've probably already noticed, most of the ways you do this work, but I would recommend using your first approach. While it may look somewhat repetitive, it is very clear and fairly easy to maintain. It also makes your code easy to read by anyone who is not that good at xaml.



You already know the problems with solution 2 and 3. Writing your own markup extension is a bit overkill (at least in this case) and makes the code difficult to understand. Solution 6 works great, but as you said, it makes it impossible to know what the text block is attached to inside the xaml.

+2


source


Just use # 2 and assume the DataContext is correct. This is the standard. It is the responsibility of the person using the User Control to ensure that the DataContext is set correctly.

Everything else just adds unnecessary complexity.



See ReSharper WPF Error: "Unable to resolve symbol 'MyVariable' due to unknown DataContext" .

Its common practice for anyone using a custom control to set the DataContext to RelativeSource Self

, see How do I bind to a RelativeSource Self?

0


source







All Articles