1

I'm learning WPF but I have a lot of Windows Forms background. I want to convert a WinForms custom control in which I did put a label and a textbox (making a TextField), with a property allowing to set percentage of width allocated to the label.

Now, in WPF, I'm a bit lost. Should I create a custom control that inherits from a grid and expose (how ?) the columns definition properties, or should I create a custom control that will "contain" a grid, and expose two properties "LabelWidth" and "ContentWidth", and bind the two column definitions to these properties ? (Thinking these properties would contain 1* and 3*).

Could someone show me an example of such construction to have a place to start?

1
  • If you want to compose several controls together - best way is create custom UserControl. So, second option. Commented Mar 6, 2017 at 10:32

2 Answers 2

2

You could create a UserControl with two dependency properties.

Please refer to the following sample code.

MyUserControl.xaml:

<UserControl x:Class="WpfApplication3.MyUserControl"
                 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:WpfApplication3"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="{Binding LabelWidth, RelativeSource={RelativeSource AncestorType=UserControl}}" />
            <ColumnDefinition Width="{Binding ContentWidth, RelativeSource={RelativeSource AncestorType=UserControl}}" />
        </Grid.ColumnDefinitions>
        <TextBlock Text="..." />
        <TextBox Grid.Column="1" />
    </Grid>
</UserControl>

MyUserControl.xaml.cs:

public partial class MyUserControl : UserControl
{
    public MyUserControl()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty LabelWidthProperty =
         DependencyProperty.Register("LabelWidth", typeof(System.Windows.GridLength),
         typeof(MyUserControl));

    public System.Windows.GridLength LabelWidth
    {
        get { return (System.Windows.GridLength)GetValue(LabelWidthProperty); }
        set { SetValue(LabelWidthProperty, value); }
    }

    public static readonly DependencyProperty ContentWidthProperty =
        DependencyProperty.Register("ContentWidth", typeof(System.Windows.GridLength),
            typeof(MyUserControl));

    public System.Windows.GridLength ContentWidth
    {
        get { return (System.Windows.GridLength)GetValue(ContentWidthProperty); }
        set { SetValue(ContentWidthProperty, value); }
    }
}

Sample usage:

<local:MyUserControl LabelWidth="1*" ContentWidth="5*" />

Dependency Properties Overview: https://msdn.microsoft.com/en-us/library/ms752914(v=vs.110).aspx

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for this answer but it doesn't work, setting LabelWidth to 1* and ContentWidth to 3* returns an error "Invalid value for property...". And also, this is a user control, not a custom control, meaning, from what I read until here, that it will not support styling.
If you want to support star-sizing you should change type of the dependency properties from double to GridLength and bind the Width properties of the ColumnDefinitions instead of binding the Width properties of the controls.
I have edited my answer to illustrate an example of this. And a UserControl has a Style and a Template property just like any other control.
Oh great, didn't see your answer so I posted mine, largely inspired by yours but in custom control version. I was stuck with the binding and you showed be the way with the statement RelativeSource={RelativeSource AncestorType=UserControl}. Why do lots of blog posts explain that UserControl doesn't support styling? Could you explain or should I open a new question for this?
I don't know how I am supposed to be able to answer why someone writes something on their blog...but please ask a new question if you have another issue.
0

I think I managed to achieve what I wanted to do by understanding mm8's code, in particular RelativeSource={RelativeSource AncestorType=UserControl} :

Added a custom control. FieldText.cs :

public class FieldText : Control
{
    static FieldText()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(FieldText), new FrameworkPropertyMetadata(typeof(FieldText)));
    }

    public FieldText()
    {
    }

    public static readonly DependencyProperty LabelLengthProperty =
        DependencyProperty.Register("LabelLength", typeof(GridLength),
        typeof(FieldText), new UIPropertyMetadata(new GridLength(25, GridUnitType.Star)));

    public virtual GridLength LabelLength
    {
        get { return (GridLength)GetValue(LabelLengthProperty); }
        set { SetValue(LabelLengthProperty, value); }
    }

    public static readonly DependencyProperty ContentLengthProperty =
        DependencyProperty.Register("ContentLength", typeof(GridLength),
        typeof(FieldText), new UIPropertyMetadata(new GridLength(75, GridUnitType.Star)));

    public virtual GridLength ContentLength
    {
        get { return (GridLength)GetValue(ContentLengthProperty); }
        set { SetValue(ContentLengthProperty, value); }
    }
}

Generic.xaml :

<Style TargetType="{x:Type controls:FieldText}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type controls:FieldText}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <Grid x:Name="grd" Margin="3px">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="{Binding Path=LabelLength, RelativeSource={RelativeSource AncestorType=Control}}" />
                            <ColumnDefinition Width="{Binding Path=ContentLength, RelativeSource={RelativeSource AncestorType=Control}}" />
                        </Grid.ColumnDefinitions>
                        <Label x:Name="label" Grid.Column="0" Content="Field:" />
                        <TextBox x:Name="textbox" Grid.Column="1" MaxLines="1" TextWrapping="NoWrap" />
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Sample usage:

<controls:FieldText x:Name="fld1" LabelLength="25*" ContentLength="75*" />

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.