Ilya Fetisov
Ilya Fetisov

Reputation: 3

WPF pass parameter to ControlTemplate

Can someone help me with such task. I have resource template and trying to decrease amount of duplicate code in it

<ControlTemplate x:Key="PlanAssetValue" TargetType="ContentControl">
    <ContentControl Template="{StaticResource Period}">
        <StackPanel Height="Auto" Width="Auto" x:Name="MainPanel">
            <DockPanel
                HorizontalAlignment="Stretch" 
                VerticalAlignment="Top" 
                Height="Auto" 
                Margin="10">
                <TextBlock Text="{Binding Path=Paramprompt[IsMarket]}" DockPanel.Dock="Left" Width="160" Margin="0,2,0,0"/>
                <ComboBox DockPanel.Dock="Left" Margin="5,0,0,0" Width="Auto" 
                                 ItemsSource="{Binding Path=ParamDetails[IsMarket]}"
                                 DisplayMemberPath="Name" 
                                 VerticalAlignment="Top" 
                                 StaysOpenOnEdit="True" 
                                 SelectedValue="{Binding Path=ParamValues[IsMarket]}" SelectedValuePath="Code">
                </ComboBox>
            </DockPanel>
            <DockPanel
                HorizontalAlignment="Stretch" 
                VerticalAlignment="Top" 
                Height="Auto" 
                Margin="10">
                <TextBlock Text="{Binding Path=Paramprompt[Currency]}" DockPanel.Dock="Left" Width="160" Margin="0,2,0,0"/>
                <ComboBox DockPanel.Dock="Left" Margin="5,0,0,0" Width="Auto" 
                                 ItemsSource="{Binding Path=ParamDetails[Currency]}"
                                 DisplayMemberPath="Name" 
                                 VerticalAlignment="Top" 
                                 StaysOpenOnEdit="True" 
                                 SelectedValue="{Binding Path=ParamValues[Currency]}" SelectedValuePath="Code">
                </ComboBox>
            </DockPanel>
        </StackPanel>
    </ContentControl>
</ControlTemplate>

I want to declare some text as template with parameter to set instead of field IsMarket

<ControlTemplate x:Key="ParamCombobox" TargetType="ContentControl">
            <DockPanel
                HorizontalAlignment="Stretch" 
                VerticalAlignment="Top" 
                Height="Auto" 
                Margin="10">
                <TextBlock Text="{Binding Path=Paramprompt[IsMarket]}" DockPanel.Dock="Left" Width="160" Margin="0,2,0,0"/>
                <ComboBox DockPanel.Dock="Left" Margin="5,0,0,0" Width="Auto" 
                                 ItemsSource="{Binding Path=ParamDetails[IsMarket]}"
                                 DisplayMemberPath="Name" 
                                 VerticalAlignment="Top" 
                                 StaysOpenOnEdit="True" 
                                 SelectedValue="{Binding Path=ParamValues[IsMarket]}" SelectedValuePath="Code">
                </ComboBox>
            </DockPanel>
</ControlTemplate>

Upvotes: 1

Views: 2631

Answers (2)

dovid
dovid

Reputation: 6491

ControleTemplate its presentation of a logic control. the ContentControl logic its present one content. It has a property named Content and you can use this "Parameter" in the template via TemplateBinding: {TemplateBinding Content}.

You want a control that represents more than single information. So create it!

Example:

class MyUiControl : Control
{
    public string Header
    {
        get { return (string)GetValue(HeaderProperty); }
        set { SetValue(HeaderProperty, value); }
    }

    public static readonly DependencyProperty HeaderProperty =
        DependencyProperty.Register("Header", typeof(string), typeof(MyUiControl), new PropertyMetadata(null));



    public IEnumerable ItemsCombo
    {
        get { return (IEnumerable)GetValue(ItemsComboProperty); }
        set { SetValue(ItemsComboProperty, value); }
    }

    public static readonly DependencyProperty ItemsComboProperty =
        DependencyProperty.Register("ItemsCombo", typeof(IEnumerable), typeof(MyUiControl), new PropertyMetadata(null));


    public object Value
    {
        get { return (IEnumerable)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(object), typeof(MyUiControl), new PropertyMetadata(null));
}

and now the template:

<ControlTemplate x:Key="ParamCombobox" TargetType="local:MyUiControl">
    <DockPanel
        HorizontalAlignment="Stretch" 
        VerticalAlignment="Top" 
        Height="Auto" 
        Margin="10">
        <TextBlock Text="{TemplateBinding Header}" DockPanel.Dock="Left" Width="160" Margin="0,2,0,0"/>
        <ComboBox DockPanel.Dock="Left" Margin="5,0,0,0" Width="Auto" 
                            ItemsSource="{TemplateBinding ItemsCombo}"
                            DisplayMemberPath="Name" 
                            VerticalAlignment="Top" 
                            StaysOpenOnEdit="True" 
                            SelectedValue="{TemplateBinding Value}" SelectedValuePath="Code">
        </ComboBox>
    </DockPanel>
</ControlTemplate>

and the control:

<local:MyUiControl Header="{Binding paramA}"
                   ItemsCombo="{Binding paramB}"
                   Value="{Binding paramC}" />

BUT, you don't really need special logic. your business it's simply a set of a label with a control. for this, i think you can use a simple HeaderedContentControl with your Template:

<Style TargetType="HeaderedContentControl">
    <Style.Setters>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="HeaderedContentControl">
                    <DockPanel>
                        <TextBlock Text="{TemplateBinding Header}" DockPanel.Dock="Left" Width="160" Margin="0,2,0,0"/>
                        <ContentPresenter DockPanel.Dock="Left" Margin="5,0,0,0" Width="Auto"  VerticalAlignment="Top" />
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style.Setters>
</Style>

the control:

<HeaderedContentControl Header="{Binding paramA}">
    <ComboBox ItemsSource="{Binding paramB}"
                DisplayMemberPath="Name" 
                StaysOpenOnEdit="True" 
                SelectedValue="{Binding paramC}" SelectedValuePath="Code" />
</HeaderedContentControl>

Upvotes: 2

Michal Ciechan
Michal Ciechan

Reputation: 13898

Wrap those 3 related objects in a class e.g.

N.B. I have omitted INotifyPropertyChanged implementations for brevity

public class ParamViewModel : INotifyPropertyChanged
{
    public string Prompt { get; set; }
    public ObservableCollection<string> Details { get; set;}
    public string SelectedValue { get; set; }
}

Then have your control template DataContext bind to the correct object in your parent VM:

<ContentControl Template="{StaticResource Period}">
    <StackPanel Height="Auto" Width="Auto" x:Name="MainPanel">
          <ContentControl DataContext="{Binding Params[IsMarket]}"
                          Template="{StaticResource ParamTemplate}" />
          <ContentControl DataContext="{Binding Params[Currency]}"
                          Template="{StaticResource ParamTemplate}" />
    </StackPanel>
</ContentControl>

And your parent VM could be something like:

public class ParentViewModel : INotifyPropertyChanged
{
    public Dictionary<string, Param> Params { get; set;}
}


<ControlTemplate x:Key="ParamTemplate" TargetType="ContentControl">
            <DockPanel
                HorizontalAlignment="Stretch" 
                VerticalAlignment="Top" 
                Height="Auto" 
                Margin="10">
                <TextBlock Text="{Binding Path=Prompt}" DockPanel.Dock="Left" Width="160" Margin="0,2,0,0"/>
                <ComboBox DockPanel.Dock="Left" Margin="5,0,0,0" Width="Auto" 
                                 ItemsSource="{Binding Path=Details}"
                                 DisplayMemberPath="Name" 
                                 VerticalAlignment="Top" 
                                 StaysOpenOnEdit="True" 
                                 SelectedValue="{Binding SelectedValue}" SelectedValuePath="Code">
                </ComboBox>
            </DockPanel>
</ControlTemplate>

Upvotes: 2

Related Questions