WPF style setter not working

2020-04-17 08:06发布

I have a custom user control that contains a combo box. I've added a ComboBoxWidth dependency property to allow developer to set width if they want. Using a style setter, I'd like to set the widths for all these combo boxes to the same value on another user control for size consistency. But, it is not working. It works if I set the size separately on each control. When the size is specified in the style setter, it is ignored. If I change the Property string from "ComboBoxWidth" to "Width", the entire width of all the controls are changed. So, it looks like the style formatting is correct. Am I missing something? This is the first time I've tried to apply a style to my own custom dependency property.

Note: AngleUserControl is based off a generic user control (that doesn't include any xaml--control created in code). The ComboBoxWidth proberty is in the generic base class. I'm not sure if that has anything to do with it or not.

Style code (in user control containing several AngleUserControl controls):

<UserControl.Resources>
    <Style TargetType="wpfControls:AngleUserControl">
        <Setter Property="ComboBoxWidth" Value="400"/>
    </Style>
</UserControl.Resources>

UnitControlBase:

/// <summary>
/// Control that displays value in different units depending on selected unit type.
/// </summary>
/// <typeparam name="TSelectionTypeEnum">The enumeration type for all the available units.</typeparam>
/// <typeparam name="TConverterType">The MultiValueConverter that converts the value between the different types of units.</typeparam>
/// <typeparam name="TValueType">The underlying type of the stored value.</typeparam>
public class UnitControlBase<TSelectionTypeEnum, TConverterType, TValueType> : UserControl
    where TSelectionTypeEnum : struct, IConvertible
    where TConverterType : IMultiValueConverter, new()
{
    #region Private Fields

    // Metadata for the dependency properties.
    private static FrameworkPropertyMetadata valuePropertyMetadata = new FrameworkPropertyMetadata(default(TValueType));
    private static FrameworkPropertyMetadata valueTypePropertyMetadata = new FrameworkPropertyMetadata(default(TSelectionTypeEnum));
    private static FrameworkPropertyMetadata displayValueTypePropertyMetadata = new FrameworkPropertyMetadata(default(TSelectionTypeEnum));
    private static FrameworkPropertyMetadata comboBoxWidthPropertyMetadata = new FrameworkPropertyMetadata(0.0);
    private static FrameworkPropertyMetadata valueFormatPropertyMetadata = new FrameworkPropertyMetadata(string.Empty);

    #endregion

    #region Constructor

    /// <summary>
    /// Constructor
    /// </summary>
    public UnitControlBase()
    {
        ValueFormat = "#,##0.00";
        ComboBoxWidth = 75.0;

        // Create main grid and add to control.
        Grid mainGrid = new Grid();
        mainGrid.Name = "LayoutRoot";
        this.AddChild(mainGrid);

        // Create grid columns.
        ColumnDefinition col1 = new ColumnDefinition();
        col1.Width = GridLength.Auto;
        ColumnDefinition col2 = new ColumnDefinition();
        mainGrid.ColumnDefinitions.Add(col1);
        mainGrid.ColumnDefinitions.Add(col2);

        // Create the text box that will display the value.
        Label displayValueLabel = new Label();
        displayValueLabel.Name = "DisplayValueLabel";
        Grid.SetColumn(displayValueLabel, 0);
        mainGrid.Children.Add(displayValueLabel);

        // Bind to the multi-value converter that will convert between the types.
        MultiBinding mb = new MultiBinding();
        mb.Converter = new TConverterType();
        mb.Bindings.Add(new Binding("Value") { Source = this });
        mb.Bindings.Add(new Binding("ValueType") { Source = this });
        mb.Bindings.Add(new Binding("DisplayValueType") { Source = this });
        mb.Bindings.Add(new Binding("ValueFormat") { Source = this });
        displayValueLabel.SetBinding(Label.ContentProperty, mb);
        displayValueLabel.HorizontalContentAlignment = System.Windows.HorizontalAlignment.Right;            

        // Create the combo box that will display selected unit.
        ComboBox displayValueComboBox = new ComboBox();
        displayValueComboBox.Name = "DisplayValueComboBox";
        displayValueComboBox.SetBinding(ComboBox.WidthProperty, new Binding("ComboBoxWidth") { Source = this });
        Grid.SetColumn(displayValueComboBox, 1);
        mainGrid.Children.Add(displayValueComboBox);

        // Bind available units and selected units.
        displayValueComboBox.ItemsSource = Enum.GetValues(typeof(TSelectionTypeEnum));
        displayValueComboBox.SetBinding(ComboBox.SelectedItemProperty, new Binding("DisplayValueType") { Source = this });
    }

    #endregion

    #region Dependency Properties

    /// <summary>
    /// Value Dependency Property
    /// </summary>
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(TValueType), typeof(UnitControlBase<TSelectionTypeEnum, TConverterType, TValueType>), valuePropertyMetadata);

    /// <summary>
    /// Value Type Dependency Property
    /// </summary>
    public static readonly DependencyProperty ValueTypeProperty =
        DependencyProperty.Register("ValueType", typeof(TSelectionTypeEnum), typeof(UnitControlBase<TSelectionTypeEnum, TConverterType, TValueType>), valueTypePropertyMetadata);

    /// <summary>
    /// Display Value Type Dependency Property
    /// </summary>
    public static readonly DependencyProperty DisplayValueTypeProperty =
        DependencyProperty.Register("DisplayValueType", typeof(TSelectionTypeEnum), typeof(UnitControlBase<TSelectionTypeEnum, TConverterType, TValueType>), displayValueTypePropertyMetadata);

    /// <summary>
    /// Combo Box Width Dependency Property
    /// </summary>
    public static readonly DependencyProperty ComboBoxWidthProperty =
        DependencyProperty.Register("ComboBoxWidth", typeof(double), typeof(UnitControlBase<TSelectionTypeEnum, TConverterType, TValueType>), comboBoxWidthPropertyMetadata);

    /// <summary>
    /// Value Format Dependency Property
    /// </summary>
    public static readonly DependencyProperty ValueFormatProperty =
        DependencyProperty.Register("ValueFormat", typeof(string), typeof(UnitControlBase<TSelectionTypeEnum, TConverterType, TValueType>), valueFormatPropertyMetadata);

    #endregion

    #region Public Properties

    /// <summary>
    /// The underlying stored value.
    /// </summary>
    public TValueType Value
    {
        get
        {
            return (TValueType)GetValue(ValueProperty);
        }
        set
        {
            SetValue(ValueProperty, value);
        }
    }

    /// <summary>
    /// The unit type for the underlying stored value.
    /// </summary>
    public TSelectionTypeEnum ValueType
    {
        get
        {
            return (TSelectionTypeEnum)GetValue(ValueTypeProperty);
        }
        set
        {
            SetValue(ValueProperty, value);
        }
    }

    /// <summary>
    /// The unit type for the displayed value.
    /// </summary>
    public TSelectionTypeEnum DisplayValueType
    {
        get
        {
            return (TSelectionTypeEnum)GetValue(DisplayValueTypeProperty);
        }
        set
        {
            SetValue(DisplayValueTypeProperty, value);
        }
    }

    /// <summary>
    /// Width of combo box displaying available units.
    /// </summary>
    public double ComboBoxWidth
    {
        get
        {
            return (double)GetValue(ComboBoxWidthProperty);
        }
        set
        {
            SetValue(ComboBoxWidthProperty, value);
        }
    }

    /// <summary>
    /// The format of the displayed value.
    /// </summary>
    public string ValueFormat
    {
        get
        {
            return (string)GetValue(ValueFormatProperty);
        }
        set
        {
            SetValue(ValueFormatProperty, value);
        }
    }

    #endregion
}

AngleUserControl.cs

/// <summary>
/// Control allowing user to display a value in degrees, radians, or semicircles.
/// </summary>
public class AngleUserControl : UnitControlBase<AngleSelectionType, AngleMultiValueConverter, double>
{
    #region Constructor

    /// <summary>
    /// Constructor.
    /// </summary>
    public AngleUserControl()
    {
        this.ComboBoxWidth = 175.0;
    }

    #endregion
}

1条回答
Evening l夕情丶
2楼-- · 2020-04-17 08:38

A so-called "local value" of a dependency property, like

this.ComboBoxWidth = 175.0;

has higher value precedence than a value from a Style Setter, like

<Setter Property="ComboBoxWidth" Value="400"/>

Hence the Style Setter has no effect.

You should assign a new default value by overriding dependency property metadata:

public class AngleUserControl : ...
{
    static AngleUserControl()
    {
        ComboBoxWidthProperty.OverrideMetadata(
            typeof(AngleUserControl),
            new PropertyMetadata(175d));
    }
}

See Dependency Property Value Precedence for reference.

查看更多
登录 后发表回答