6
votes

Let's say I have a custom control which wraps another control (for example MyCustomButton). I expose a property Content, which wraps the inner control:

    public object Content
    {
        get { return innerControl.Content; }
        set { innerControl.Content = value; }
    }

In order for a consumer to bind to this property, I need to define a DependencyProperty for it:

 public static DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof (object), typeof (MyCustomButton));

but now I need my property definition to use GetValue/SetValue:

    public object Content
    {
        get { return GetValue(ContentProperty); }
        set { SetValue(ContentProperty, value); }
    }

so I'm not wrapping the value of the inner control anymore.

I can define PropertyMetadata to handle the PropertyChanged event of the DependencyProperty, but then I need a bunch of plumbing code to keep the values in sync and prevent infinite loopbacks on changed.

UPDATE: I can't just derive from Button because my UserControl has various other concerns.

Is there a better way to do this?

1

1 Answers

2
votes

Well, depending on the particulars of why you're wrapping a button with a user control, you could define a custom control that inherits from button. Then, instead of wrapping the button and exposing the wrapped methods and properties that you want, you can simply override methods and properties whose behavior you want to define the custom control. This way, you'll get all of the functionality of button without the need to reinvent the wheel.

Here's a google link that walks you through it (one of the first that I found - there are plenty): http://knol.google.com/k/creating-custom-controls-with-c-net#

If the user control has other concerns, this may not be an option for you, but I'm offering this answer because the only purpose that you've mentioned for it is wrapping the button. I'd personally favor creating a custom control and inheriting rather than a user control and wrapping if the control in question is simply meant to be a more specific kind of wrapped/inherited control (i.e. button in your case).

Edit: In light of updated question...

You could do something along these lines. Here is the XAML of the client of your user control:

<Grid>
    <local:MyControl ButtonContent="Click Me!"/>
</Grid>
</Window>

Here is the XAML for the user control itself:

 <UserControl x:Class="GuiScratch.MyControl"
             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:GuiScratch"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>

        <StackPanel>
            <ContentControl Content="Asdf"/>
            <Button Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyControl}},Path=ButtonContent}"/>
        </StackPanel>

    </Grid>
</UserControl>

And, here is the code behind:

public partial class MyControl : UserControl
{

    public static readonly DependencyProperty ButtonContentProperty = 
    DependencyProperty.Register("ButtonContent", typeof(object), typeof(MyControl), 
    new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));

    public object ButtonContent
    {
        get { return (object)GetValue(ButtonContentProperty); }
        set { SetValue(ButtonContentProperty, value); }
    }

    public MyControl()
    {
        InitializeComponent();
    }
}

So, you don't need to handle the binding at all through code. Your client XAML binds to your dependency property, as does the XAML of the user control itself. In this fashion, they share the dependency property setting. I ran this in my little scratchpad, and the result is (at least my understanding of) what you're looking for. The main window displays the user control as a stack panel with the text "Asdf" and then a button with the text "Click Me!"