user1192724
user1192724

Reputation: 529

How to create resuable control and set the properties of nested controls using common template/control in xaml

I have tried to google this but I am not super clear on the suggestions I see people making. I have 3 buttons within a user control that are exactly the same except for automation id and text. Instead of duplicating this code across all buttons, I would like to create a common control or control template that I can use. My problem is these buttons have nested controls whose text I need to change but I'm not sure how to do this.

<UserControl>

<Grid>       
        <StackPanel x:Name="myStackPanel" Grid.Row="1" Padding="0">
            <Button AutomationProperties.AutomationId="CallButton" x:Name="CallButton" Style="{StaticResource ButtonStyle}">
                <RelativePanel>
                    <SymbolIcon AutomationProperties.AutomationId="CallIcon" x:Name="CallIcon" Symbol="Phone" Style="{StaticResource SymbolStyle}" />
                    <StackPanel>
                        <TextBlock AutomationProperties.AutomationId="CallLabel" Text="Call" Style="{StaticResource LabelStyle}"/>
                        <TextBlock AutomationProperties.AutomationId="CallText" Text="123-456-7890" Style="{StaticResource ContentStyle}"/>
                    </StackPanel>
                </RelativePanel>
            </Button>
            <Button AutomationProperties.AutomationId="EmailButton" x:Name="EmailButton" Style="{StaticResource ButtonStyle}">
                <RelativePanel>
                    <SymbolIcon AutomationProperties.AutomationId="EmailIcon" x:Name="EmailIcon" Symbol="Mail" Style="{StaticResource SymbolStyle}"/>
                    <StackPanel >
                        <TextBlock AutomationProperties.AutomationId="EmailLabel" Text="Email" Style="{StaticResource LabelStyle}"/>
                        <TextBlock AutomationProperties.AutomationId="EmailText" Text="[email protected]" Style="{StaticResource ContentStyle}"/>
                    </StackPanel>
                </RelativePanel>
            </Button>
            <Button AutomationProperties.AutomationId="WebsiteButton" x:Name="WebsiteButton" Style="{StaticResource ButtonStyle}">
                <RelativePanel>
                    <SymbolIcon AutomationProperties.AutomationId="WebsiteIcon" x:Name="WebsiteIcon" Symbol="Link" Style="{StaticResource SymbolStyle}"/>
                    <StackPanel >
                        <TextBlock AutomationProperties.AutomationId="WebsiteLabel" Text="Website" Style="{StaticResource LabelStyle}"/>
                        <TextBlock AutomationProperties.AutomationId="WebsiteText" Text="http://meetme.com" Style="{StaticResource ContentStyle}"/>
                    </StackPanel>
                </RelativePanel>
            </Button>
        </StackPanel>          
    </Grid>
</Grid>

As you can see, the code for all 3 buttons is same. All I want to do is create a control where I can set the automation ids and text properties of the nested controls.

Thanks!

Upvotes: 3

Views: 764

Answers (2)

Max
Max

Reputation: 1820

You can create a button based on a UserControl (add a new UserControl). It will allow you to enjoy all the default button's properties, events and states (OnClick, Command, etc.) and to add your own properties, template and behavior.

Using dependency properties instead of simple properties is strongly advised if you want to use bindings or animations on them.

C#:

public partial class CustomButton : Button
{

    #region IconAutomationId

    public string IconAutomationId
    {
        get { return (string)GetValue(IconAutomationIdProperty); }
        set { SetValue(IconAutomationIdProperty, value); }
    }

    public static readonly DependencyProperty IconAutomationIdProperty =
        DependencyProperty.Register("IconAutomationId", typeof(string), typeof(CustomButton), new PropertyMetadata(null));

    #endregion

    #region LabelAutomationId

    public string LabelAutomationId
    {
        get { return (string)GetValue(LabelAutomationIdProperty); }
        set { SetValue(LabelAutomationIdProperty, value); }
    }

    public static readonly DependencyProperty LabelAutomationIdProperty =
        DependencyProperty.Register("LabelAutomationId", typeof(string), typeof(CustomButton), new PropertyMetadata(null));

    #endregion

    #region TextAutomationId

    public string TextAutomationId
    {
        get { return (string)GetValue(TextAutomationIdProperty); }
        set { SetValue(TextAutomationIdProperty, value); }
    }

    public static readonly DependencyProperty TextAutomationIdProperty =
        DependencyProperty.Register("TextAutomationId", typeof(string), typeof(CustomButton), new PropertyMetadata(null));

    #endregion

    #region Symbol

    public object Symbol
    {
        get { return (object)GetValue(SymbolProperty); }
        set { SetValue(SymbolProperty, value); }
    }

    public static readonly DependencyProperty SymbolProperty =
        DependencyProperty.Register("Symbol", typeof(object), typeof(CustomButton), new PropertyMetadata(null));

    #endregion

    #region Label

    public string Label
    {
        get { return (string)GetValue(LabelProperty); }
        set { SetValue(LabelProperty, value); }
    }

    public static readonly DependencyProperty LabelProperty =
        DependencyProperty.Register("Label", typeof(string), typeof(CustomButton), new PropertyMetadata(null));

    #endregion

    #region Text

    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(CustomButton), new PropertyMetadata(null));

    #endregion

    public CustomButton()
    {
        InitializeComponent();
    }
}

XAML:

<Button x:Class="WpfApplication1.CustomButton"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Name="customButton">

    <RelativePanel>
        <SymbolIcon AutomationProperties.AutomationId="{Binding IconAutomationId, ElementName=customButton}"
                    Symbol="{Binding Symbol, ElementName=customButton}"
                    Style="{StaticResource SymbolStyle}"/>
        <StackPanel >
            <TextBlock AutomationProperties.AutomationId="{Binding LabelAutomationId, ElementName=customButton}"
                       Text="{Binding Label, ElementName=customButton}"
                       Style="{StaticResource LabelStyle}"/>
            <TextBlock AutomationProperties.AutomationId="{Binding TextAutomationId, ElementName=customButton}"
                       Text="{Binding Text, ElementName=customButton}"
                       Style="{StaticResource ContentStyle}"/>
        </StackPanel>
    </RelativePanel>
</Button>

Use:

<local:CustomButton AutomationProperties.AutomationId="CallButton"
                    x:Name="CallButton"
                    Style="{StaticResource ButtonStyle}"
                    IconAutomationId="CallIcon"
                    LabelAutomationId="CallLabel"
                    TextAutomationId="CallText"
                    Symbol="Phone"
                    Label="Call"
                    Text="123-456-7890"/>

Upvotes: 1

karfus
karfus

Reputation: 939

It may be more work than you feel like doing but it sounds like you're going to want to create a UserControl. It should inherit from Button in the code-behind:

public partial class MyButton: Button

In the XAML you will include essentially the guts of what you have in each of the buttons now.

The tedious part is that you will then (in the code-behind) need to create a DependencyProperty for each of the "properties" you will want to set in this control: for example, one for the CallIconAutomationId, one for the CallLabelAutomationId, one for the CallLabelText, etc. You will then bind each of these properties in the XAML to the dependency property. These properties become the data that you will set on each individual MyButton (your new UserControl).

Then, in the container that is hosting these controls (which appears to be another UserControl in your example above) you would set these custom properties on each of your new MyButton controls, which will look something like this:

<myNamespace:MyButton EmailIconAutomationId="EmailIcon" LabelAutomationId="EmailLabel" />

etc.

Basically, you're creating a new control (a UserControl) based on the Button control (which gives you most of your functionality) and adding new custom properties directly to that new control (which work just like all the other control properties you're accustomed to).

Upvotes: 1

Related Questions