WPF 自定义控件,将多个 ComboBox 值组合成单个 DependencyProperty

WPF Custom Control, Combine multiple ComboBox values into single DependencyProperty

我有一个带有两个 ComboBox 的自定义控件,其中包含硬编码的 ComboBoxItems,我希望能够通过单个 DependencyProperty 绑定到这些值,但似乎无法使绑定正常工作。也许我的方法有点离谱,但我应该怎么做才能让它发挥作用?

Generic.xaml:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyApp.Components">
    <Style TargetType="{x:Type local:ContractLimitSelector}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ContractLimitSelector}">
                    <ControlTemplate.Resources>
                        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
                    </ControlTemplate.Resources>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="auto" />
                            <ColumnDefinition Width="auto" />
                        </Grid.ColumnDefinitions>
                        <ComboBox x:Name="PART_ComboAA" Grid.Column="0" SelectedValue="{TemplateBinding SelectedAA}" />
                        <ComboBox x:Name="PART_ComboFA" Grid.Column="1" SelectedValue="{TemplateBinding SelectedFA}" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

自定义控件:

public class SelectorItems
{
    public string AA { get; set; }
    public string FA { get; set; }
}

public class ContractLimitSelector : Control
{
    private static List<string> ComboBoxSource = new List<string>()
    {
        "-",
        "BE",
        "EE",
    };

    static ContractLimitSelector()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ContractLimitSelector), new FrameworkPropertyMetadata(typeof(ContractLimitSelector)));
    }

    public override void OnApplyTemplate()
    {
        var comboAA = Template.FindName("PART_ComboAA", this) as ComboBox;
        var comboFA = Template.FindName("PART_ComboFA", this) as ComboBox;

        comboAA.ItemsSource = ComboBoxSource;
        comboFA.ItemsSource = ComboBoxSource;

        base.OnApplyTemplate();
    }

    private SelectorItems GetAllValues()
    {
        var AA = (string)GetValue(SelectedAAProperty);
        var FA = (string)GetValue(SelectedFAProperty);

        return new SelectorItems() { AA = AA, FA = FA };
    }

    public static readonly DependencyProperty VisibleHeaderProperty =
        DependencyProperty.Register("VisibleHeader", typeof(bool), typeof(ContractLimitSelector),
            new PropertyMetadata(false));

    public bool VisibleHeader
    {
        get { return (bool)GetValue(VisibleHeaderProperty); }
        set { SetValue(VisibleHeaderProperty, value); }
    }

    public static readonly DependencyProperty SelectedAAProperty =
        DependencyProperty.Register("SelectedAA", typeof(string), typeof(ContractLimitSelector),
            new PropertyMetadata(string.Empty));

    public string SelectedAA
    {
        get { return (string)GetValue(SelectedAAProperty); }
        set
        {
            var all = GetAllValues();
            SetValue(SelectedValuesProperty, new SelectorItems() { AA = value, FA = all.FA, });
            SetValue(SelectedAAProperty, value);
        }
    }

    public static readonly DependencyProperty SelectedFAProperty =
        DependencyProperty.Register("SelectedFA", typeof(string), typeof(ContractLimitSelector),
            new PropertyMetadata(string.Empty));

    public string SelectedFA
    {
        get { return (string)GetValue(SelectedFAProperty); }
        set
        {
            var all = GetAllValues();
            SetValue(SelectedValuesProperty, new SelectorItems() { AA = all.AA, FA = value, });
            SetValue(SelectedFAProperty, value);
        }
    }

    public SelectorItems SelectedValues
    {
        get
        {
            return new SelectorItems()
            {
                AA = (string)GetValue(SelectedAAProperty),
                FA = (string)GetValue(SelectedFAProperty),
            };
        }
        set
        {
            SetValue(SelectedAAProperty, value.AA);
            SetValue(SelectedFAProperty, value.FA);
        }
    }

    public static readonly DependencyProperty SelectedValuesProperty =
        DependencyProperty.Register("SelectedValues", typeof(SelectorItems), typeof(ContractLimitSelector),
            new PropertyMetadata(new SelectorItems()));
}

我是这样使用的: XAML:

<custom:ContractLimitSelector SelectedValues="{Binding PrepareLockHouseValue, Mode=TwoWay}" />

视图模型:

private SelectorItems _contractValues;
public SelectorItems ContractValues
{
    get
    {
        return _contractValues;
    }
    set
    {
        _contractValues = value;
        OnPropertyChanged(nameof(ContractValues));
    }
}

我认为每当组合框的任何更改都会触发“共享”SelectedValues DependecyProperty 时使用 SetValue,但没有任何东西被触发...

我错过了什么?

当您调用

时,SelectedAASelectedFA属性设置器不会被调用
SetValue(SelectedAAProperty, value.AA);
SetValue(SelectedFAProperty, value.FA);

你必须写

SelectedAA = value.AA;
SelectedFA = value.FA;

最好移动除

以外的所有代码
SetValue(SelectedAAProperty, value);

SetValue(SelectedFAProperty, value);

在 setter 之外并添加 PropertyChangedCallbacks 以执行其他代码。

这些属性也可以正确地与数据绑定和其他可能的值源一起使用。


通常,依赖项 属性 的 CLR 包装器的 get 和 set 方法不能包含除 GetValueSetValue 调用之外的任何内容,因为 get/set 可能会被框架绕过。

详情见XAML Loading and Dependency Properties