Labeled ComboBox in Windows 10 Universal App

similar to my Labeled TextBox, which issues are resolved in:

Labeled TextBox in Windows Universal App

I got two issues in my Labeled Combobox, but first the Code:

Generic.xaml:

<Style TargetType="template:LabeledComboBox">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="template:LabeledComboBox">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <TextBlock Text="{TemplateBinding Label}" FontWeight="Bold" VerticalAlignment="Center" Margin="10,0" />
                    <ComboBox x:Name="PART_ComboBox" ItemsSource="{TemplateBinding ItemsSource}" SelectedIndex="{TemplateBinding SelectedIndex}" SelectedValue="{TemplateBinding SelectedValue}" SelectedValuePath="{TemplateBinding SelectedValuePath}" DisplayMemberPath="{TemplateBinding DisplayMemberPath}" VerticalAlignment="Center" Margin="20,0,10,0" Grid.Row="1" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

LabeledComboBox.cs:

[TemplatePart(Name = "PART_ComboBox", Type = typeof(ComboBox))]
public sealed class LabeledComboBox : Control, IParameterReturnable
{
    public static readonly DependencyProperty LabelProperty = DependencyProperty.Register("Label", typeof(string), typeof(LabeledComboBox), new PropertyMetadata(""));
    public string Label
    {
        get { return GetValue(LabelProperty).ToString(); }
        set { SetValue(LabelProperty, value); }
    }

    public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(object), typeof(LabeledComboBox), new PropertyMetadata(null));
    public object ItemsSource
    {
        get { return GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }

    public static readonly DependencyProperty SelectedIndexProperty = DependencyProperty.Register("SelectedIndex", typeof(int), typeof(LabeledComboBox), new PropertyMetadata(default(int)));
    public int SelectedIndex
    {
        get { return (int) GetValue(SelectedIndexProperty); }
        set { SetValue(SelectedIndexProperty, value); }
    }

    public static readonly DependencyProperty SelectedValueProperty = DependencyProperty.Register("SelectedValue", typeof(object), typeof(LabeledComboBox), new PropertyMetadata(null));
    public object SelectedValue
    {
        get { return GetValue(SelectedValueProperty); }
        set { SetValue(SelectedValueProperty, value); }
    }

    public static readonly DependencyProperty SelectedValuePathProperty = DependencyProperty.Register("SelectedValuePath", typeof(string), typeof(LabeledComboBox), new PropertyMetadata(default(string)));
    public string SelectedValuePath
    {
        get { return GetValue(SelectedValuePathProperty).ToString(); }
        set { SetValue(SelectedValuePathProperty, value); }
    }

    public static readonly DependencyProperty DisplayMemberPathProperty = DependencyProperty.Register("DisplayMemberPath", typeof(string), typeof(LabeledComboBox), new PropertyMetadata(default(string)));
    public string DisplayMemberPath
    {
        get { return GetValue(DisplayMemberPathProperty).ToString(); }
        set { SetValue(DisplayMemberPathProperty, value); }
    }

    private ComboBox _comboBox;

    public LabeledComboBox()
    {
        this.DefaultStyleKey = typeof(LabeledComboBox);
    }

    public LabeledComboBox(List<Parameter> parameterList)
    {
        this.Label = parameterList[0].DisplayName ?? "";
        this.ItemsSource = parameterList;
        this.SelectedValuePath = "DefaultValue";
        this.DisplayMemberPath = "DefaultValue";
        this.SelectedIndex = 0;
        this.DefaultStyleKey = typeof(LabeledComboBox);
    }

    protected override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        _comboBox = GetTemplateChild("PART_ComboBox") as ComboBox;

        if (_comboBox != null)
        {
            _comboBox.SelectionChanged += OnComboBoxSelectionChanged;
            if (_comboBox.Items != null)
            {
                this.SelectedIndex = 0;
                _comboBox.SelectedValue = _comboBox.Items[this.SelectedIndex];
            }
        }
    }

    private void OnComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        this.SelectedValue = _comboBox.SelectedValue;
    }

    public string GetKey()
    {
        return Label;
    }

    public string GetValue()
    {
        return SelectedValue.ToString();
    }

}

It will be called in two different ways:

Dynamically in C#:

stackPanel.Add(new LabeledComboBox(parameterList));

Static in Xaml:

<templates:LabeledComboBox Label="Kategorien:" ItemsSource="{Binding ElementName=pageRoot, Path=FeedCategories}" DisplayMemberPath="Name" SelectedValuePath="Name" />

As I said before I got two issues with it:

  1. How can I bind the SelectionChangedEvent to access it in Xaml || C#
  2. As you can see, I try to preselect the first Item, which does not work and I don't know how to do it right

Thank you very much for all helpful and well meant answers in advance!

Upvotes: 1

Views: 1693

Answers (1)

Kristian Vukusic
Kristian Vukusic

Reputation: 3324

Instead of creating a custom control and recreating all needed dependency properties, I would suggest you use the Header and HeaderTemplate properties of the built in ComboBox, which will be displayed, just like in your LabeledComboBox, above the selection menu. Additionally the SelectionChanged event will be available.

So the usage in XAML would look like the following:

    <ComboBox
        DisplayMemberPath="Name"
        Header="Kategorien:"
        ItemsSource="{Binding ElementName=pageRoot, Path=FeedCategories}"
        SelectedValuePath="Name"
        SelectionChanged="OnSelectionChanged">
        <ComboBox.HeaderTemplate>
            <DataTemplate>
                <TextBlock
                    Margin="10,0"
                    VerticalAlignment="Center"
                    FontWeight="Bold"
                    Text="{Binding}" />
            </DataTemplate>
        </ComboBox.HeaderTemplate>
    </ComboBox>

But if you don't want to use the above method, to expose the selection changed event in your LabeledComboBox, add the following code:

    private void OnComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        this.SelectedValue = _comboBox.SelectedValue;
        this.RaiseSelectionChanged(e);
    }

    public event EventHandler<SelectionChangedEventArgs> SelectionChanged;

    private void RaiseSelectionChanged(SelectionChangedEventArgs args)
    {
        if (SelectionChanged != null)
        {
            SelectionChanged(this, args);
        }
    }

Then you can use the created SelectionChanged event from XAML.

Upvotes: 1

Related Questions