选择行时,XAML为CustomControl设置前景

时间:2017-08-26 19:37:07

标签: c# wpf xaml datagrid

我有一个Datagrid,其中一些列是DataGridTemplateColumn,如此

<DataGridTemplateColumn 
    Width="1.5*"
    Header="{x:Static lang:Labels.GENERAL_ValorTotal}">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <sty:DecimalTextBox
                Text="{Binding Valor,StringFormat=\{0:c3\}}"
                IsReadOnly="True"
                Style="{StaticResource DecimalTextBoxGridStyle}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

enter image description here

我的问题是当我选择一行时,我的自定义控件不像DataGridTextColumn那样改变它的前景色。

如何设置自定义控件或数据网格的样式以强制更改所有列

修改1 我的自定义对象。

public class DecimalTextBox : TextBox
{

    #region Float Color 
    public static readonly DependencyProperty FloatColorProperty =
        DependencyProperty.Register("FloatColor", typeof(Color), typeof(DecimalTextBox), new FrameworkPropertyMetadata(Colors.Red));
    public Color FloatColor
    {
        get { return (Color)GetValue(FloatColorProperty); }
        set { SetValue(FloatColorProperty, value); }
    }
    #endregion

    #region Show Zero value 
    public bool ShowZeroValue
    {
        get { return (bool)GetValue(ShowZeroValueProperty); }
        set { SetValue(ShowZeroValueProperty, value); }
    }
    // Using a DependencyProperty as the backing store for ShowZeroValue.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ShowZeroValueProperty =
        DependencyProperty.Register("ShowZeroValue", typeof(bool), typeof(DecimalTextBox), new PropertyMetadata(true));
    #endregion

    protected TextBlock _textBlock;
    protected FrameworkElement _textBoxView;

    public DecimalTextBox()
    {
        _textBlock = new TextBlock() { Margin = new Thickness(1, 0, 0, 0) };
        Loaded += ExTextBox_Loaded;
    }

    private void ExTextBox_Loaded(object sender, RoutedEventArgs e)
    {
        Loaded -= ExTextBox_Loaded;

        // hide the original drawing visuals, by setting opacity on their parent
        var visual = this.GetChildOfType<DrawingVisual>();

        if (visual != null)
        {
            _textBoxView = (FrameworkElement)visual.Parent;
            _textBoxView.Opacity = 0;

            // add textblock to do the text drawing for us
            var grid = this.GetChildOfType<Grid>();
            if (grid.Children.Count >= 2)
                grid.Children.Insert(1, _textBlock);
            else
                grid.Children.Add(_textBlock);
        }
    }

    protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
    {
        base.OnLostKeyboardFocus(e);

        _textBoxView.Opacity = 0;
        _textBlock.Visibility = Visibility.Visible;
        MustShowValue();
    }

    protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
    {
        base.OnGotKeyboardFocus(e);

        _textBoxView.Opacity = 1;
        _textBlock.Visibility = Visibility.Collapsed;
    }

    private bool MustShowValue()
    {
        bool show = true;
        if (!ShowZeroValue && Text == "0")
        {
            show = false;
            _textBlock.Inlines.Clear();
            _textBlock.Inlines.Add(new Run
            {
                Text = string.Empty,
                FontFamily = FontFamily,
                FontSize = FontSize,
                Foreground = Foreground
            });

            _textBlock.Inlines.Add(new Run
            {
                Text = string.Empty,
                FontFamily = FontFamily,
                TextDecorations = System.Windows.TextDecorations.Underline,
                BaselineAlignment = BaselineAlignment.TextTop,
                FontSize = FontSize * 5 / 6,
                Foreground = new SolidColorBrush(FloatColor)
            });
        }
        return show;
    }

    protected override void OnTextChanged(TextChangedEventArgs e)
    {
        base.OnTextChanged(e);

        if (MustShowValue())
        {
            // making sure text on TextBlock is updated as per TextBox
            var dotPos = Text.IndexOf('.');
            var textPart1 = dotPos == -1 ? Text : Text.Substring(0, dotPos + 1);
            var textPart2 = (dotPos == -1 || dotPos >= (Text.Length - 1)) ? null : Text.Substring(dotPos + 1);

            _textBlock.Inlines.Clear();
            _textBlock.Inlines.Add(new Run
            {
                Text = textPart1,
                FontFamily = FontFamily,
                FontSize = FontSize,
                Foreground = Foreground
            });

            if (textPart2 != null)
                _textBlock.Inlines.Add(new Run
                {
                    Text = textPart2,
                    FontFamily = FontFamily,
                    TextDecorations = System.Windows.TextDecorations.Underline,
                    BaselineAlignment = BaselineAlignment.TextTop,
                    FontSize = FontSize * 5 / 6,
                    Foreground = new SolidColorBrush(FloatColor)
                });
        }
    }
}

public static class HelperExtensions
{
    public static T GetChildOfType<T>(this DependencyObject depObj) where T : DependencyObject
    {
        if (depObj == null) return null;

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
        {
            var child = VisualTreeHelper.GetChild(depObj, i);

            var result = (child as T) ?? GetChildOfType<T>(child);
            if (result != null) return result;
        }
        return null;
    }
}


<Style x:Key="DecimalTextBoxGridStyle" TargetType="{x:Type local:DecimalTextBox}">
    <Setter Property="TextAlignment" Value="Right"/>
    <Setter Property="FloatColor" Value="Black"/>
    <Setter Property="BorderBrush" Value="Transparent"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="ShowZeroValue" Value="False"/>
    <Style.Triggers>
        <!--<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" Value="True">
            <Setter Property="Foreground" Value="White"/>
        </DataTrigger>-->
    </Style.Triggers>
</Style>

1 个答案:

答案 0 :(得分:2)

您只需要每次TextBlockForeground更改内联颜色(内部FloatColor)。困难的方法是在TextBox - 属性和内联属性之间添加绑定。简单的方法是将属性更改的回调添加到依赖项属性,或者仅通过覆盖OnPropertyChanged

例如(说明使用OnPropertyChanged更新控件):

protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
    base.OnPropertyChanged(e);

    if (e.Property == ForegroundProperty || e.Property == FloatColorProperty)
        UpdateTextBlock(); //updates the text-block inlines
}

protected override void OnTextChanged(TextChangedEventArgs e)
{
    base.OnTextChanged(e);
    UpdateTextBlock(); // this can be merged to OnPropertyChanged (as text is also dependency property)
}

// new extracted method from OnTextChanged
private void UpdateTextBlock()
{
    if (MustShowValue())
    {
        // making sure text on TextBlock is updated as per TextBox
        var dotPos = Text.IndexOf('.');
        var textPart1 = dotPos == -1 ? Text : Text.Substring(0, dotPos + 1);
        var textPart2 = (dotPos == -1 || dotPos >= (Text.Length - 1)) ? null : Text.Substring(dotPos + 1);

        _textBlock.Inlines.Clear();
        _textBlock.Inlines.Add(new Run
        {
            Text = textPart1,
            FontFamily = FontFamily,
            FontSize = FontSize,
            Foreground = Foreground
        });

        if (textPart2 != null)
            _textBlock.Inlines.Add(new Run
            {
                Text = textPart2,
                FontFamily = FontFamily,
                TextDecorations = System.Windows.TextDecorations.Underline,
                BaselineAlignment = BaselineAlignment.TextTop,
                FontSize = FontSize * 5 / 6,
                Foreground = new SolidColorBrush(FloatColor)
            });
    }
}

同样适用于FontFamilyFontSize。这样,只要UpdateTextBlock()使用的任何属性更新 - 无论是通过样式还是触发器 - 控件都将知道它需要更新内部TextBlock中的内联。

更新08/27

此外,更新您的Style以设置{set}中的ForegroundFloatColor,同时使用MultiDataTrigger来考虑行的所选状态和网格的焦点状态。

<Style x:Key="DecimalTextBoxGridStyle" TargetType="{x:Type local:DecimalTextBox}">
    <Setter Property="TextAlignment" Value="Right"/>
    <Setter Property="Foreground" Value="Black"/>
    <Setter Property="FloatColor" Value="Black"/>
    <Setter Property="BorderBrush" Value="Transparent"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="ShowZeroValue" Value="False"/>
    <Style.Triggers>
        <MultiDataTrigger>
            <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridRow}}}" Value="True" />
                <Condition Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}, Path=IsKeyboardFocusWithin}" Value="True" />
            </MultiDataTrigger.Conditions>
            <MultiDataTrigger.Setters>
                <Setter Property="Foreground" Value="White"/>
                <Setter Property="FloatColor" Value="White"/>
            </MultiDataTrigger.Setters>
        </MultiDataTrigger>
    </Style.Triggers>
</Style>