背景颜色阻碍WPF UserControl内容

时间:2014-06-15 12:41:45

标签: c# wpf

我有一个UserControl,在XAML中引用如下:

<local:ColumnGraphRenderCtrl x:Name="graphCtrl" Grid.Column="1" 
    Height="Auto" Width="Auto"/>

有问题的UserControl有几个矩形形状,它们显示正常。

然而,如果我指定Background颜色,指定的颜色会阻碍矩形,并且只会显示颜色。例如:

<local:ColumnGraphRenderCtrl x:Name="graphCtrl" Background="Blue" Grid.Column="1" 
    Height="Auto" Width="Auto"/>

(如果我将颜色更改为&#34;透明&#34;,矩形确实可见。)

我还尝试使用ControlTemplate作为UserControl(作为Style的一部分),但我得到了相同的结果(即阻止UserControl内容的背景颜色)

我在MSDN上查找了 Control.Background 属性,该属性提供了以下注释:

  

Background属性仅适用于a的静止状态   控制。控件的默认样式指定其外观   当控制状态发生变化时。例如,如果您设置了   Button上的Background属性,该按钮仅在具有该值时   没有按下或禁用它。如果你想创建一个控件   有一个更高级的后台自定义,你必须定义   对照的风格。

     

此属性仅影响其模板使用的控件   背景属性作为参数。在其他控件上,此属性   没有影响。

MSDN中的备注有何意义,如何在不阻止控件内容的情况下指定背景颜色?

编辑:内容控件(矩形)在代码隐藏中手动添加,如果这有所不同。

UserControl代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;

namespace GraphingWithShapes
{
    public partial class ColumnGraphRenderCtrl: UserControl
    {
        private ObservableCollection<NameValuePair> _dataPoints = null;
        private List<Color> _columnColors = new List<Color>() { Colors.Blue, Colors.Red, Colors.Green };

        public ColumnGraphRenderCtrl()
        {
            InitializeComponent();
        }

        public void SetData(ObservableCollection<NameValuePair> data)
        {
            _dataPoints = data;
            _dataPoints.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_dataPoints_CollectionChanged);
            InvalidateVisual();
        }

        void _dataPoints_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            InvalidateVisual();
        }

        public double GetLargestValue()
        {
            double value = 0;

            foreach (NameValuePair nvp in _dataPoints)
            {
                value = Math.Max(value, nvp.Value);
            }

            return value;
        }

        protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
        {
            base.OnMouseDoubleClick(e);
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            if (_dataPoints != null)
            {
                double spaceToUseY = ActualHeight * 0.8;
                double spaceToUseX = ActualWidth * 0.8;
                double barWidth = spaceToUseX / _dataPoints.Count;
                double largestValue = GetLargestValue();
                double unitHeight = spaceToUseY / largestValue;

                double bottom = ActualHeight * 0.9;
                double left = ActualWidth * 0.1;

                Brush fillBrush;
                Pen outlinePen = new Pen(Brushes.Black, 1);
                int nIndex = 0;
                Rect rect;
                double height;

                foreach (NameValuePair nvp in _dataPoints)
                {
                    fillBrush = new SolidColorBrush(_columnColors[nIndex % _columnColors.Count]);

                    height = (nvp.Value * unitHeight);
                    rect = new Rect(left, bottom - height, barWidth, height);
                    drawingContext.DrawRectangle(fillBrush, outlinePen, rect);

                    left += rect.Width;
                    nIndex++;
                }
            }
        }
    }
}

3 个答案:

答案 0 :(得分:4)

为了编写一个通过覆盖OnRender方法进行渲染的自定义控件,您不应该从UserControl甚至Control派生,因为它们通过{ {1}},也使用ControlTemplate画笔填充其区域。所有这些都是在他们的OnRender方法之外完成的,所以要覆盖它而不是调用基类&#39; OnRender没有帮助。

相反,派生自BackgroundFrameworkElement,声明一个UIElement属性并在完成其余渲染之前用背景填充控件区域:

Background

您可以在MSDN上的Control Authoring Overview文章中找到更多信息。有一节关于派生自FrameworkElement。

答案 1 :(得分:1)

所以我对此的解决方案有效,但你必须摆弄宽度和高度。在用户控件中,我添加了一个视图框和一个统一的网格。

<UserControl x:Class="GraphingWithShapes.ColumnGraphRenderCtrl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Viewbox>
        <UniformGrid x:Name="GraphGrid" />
    </Viewbox>
</UserControl>

然后根据输入的数据设置它的宽度和高度。使列等于数据的计数。 (注意我没有像你那样改变图形颜色的逻辑)。然后我添加了一个Border(有一个边框和一个背景)并将其添加到unifrom网格中。 (代码在这里)

    public void SetData(ObservableCollection<NameValuePair> data)
    {
        _dataPoints = data;
        _dataPoints.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_dataPoints_CollectionChanged);
        GraphGrid.Columns = _dataPoints.Count;

        RebuildGraph();
        InvalidateVisual();
    }

    private void RebuildGraph()
    {
        GraphGrid.Children.Clear();
        GraphGrid.Height = GetLargestValue();
        GraphGrid.Width = _dataPoints.Count * 3;
        foreach (var item in _dataPoints)
        {
            AddGraphBar(item.Value);
        }
    }

    private void AddGraphBar(double value)
    {
        Border grid = new Border();
        grid.BorderBrush = Brushes.Black;
        grid.BorderThickness = new Thickness(1);
        grid.Background = Brushes.Green;
        grid.VerticalAlignment = System.Windows.VerticalAlignment.Bottom;
        grid.Height = value;
        GraphGrid.Children.Add(grid);
    }

当我在我的用户控件上添加背景颜色时,它现在可以正常工作。我希望这会有所帮助。

答案 2 :(得分:1)

如果使用Visual Studio的模板创建自定义控件,它将创建一个Generic.xaml文件,为控件提供ControlTemplate。

此模板中的默认设置使其从ControlTemplate复制Border的背景,这会导致背景(在自定义控件上无法复制地呈现)以过度绘制控件。

<ControlTemplate TargetType="{x:Type local:MyControl}">
    <Border Background="{TemplateBinding Background}">

要解决此问题,请从ControlTemplate中移除Border Background,然后渲染背景填充矩形作为您在OnRender中绘制的第一个内容:

drawingContext.DrawRectangle(Background, null, new Rect(RenderSize));