【问题标题】:Background color obstructs WPF UserControl content背景颜色阻碍 WPF UserControl 内容
【发布时间】:2014-06-15 12:41:44
【问题描述】:

我有一个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"/>

(如果我将颜色更改为“透明”,矩形就会变得可见。)

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

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

Background 属性仅适用于 控制。控件的默认样式指定其外观 当控件的状态发生变化时。例如,如果您设置 按钮上的背景属性,按钮只有在 它没有被按下或禁用。如果你想创建一个控件 有更高级的背景自定义,你必须定义 控件的样式。

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

MSDN中的注释有什么意义,如何指定背景颜色而不遮挡控件内容?

编辑:内容控件(矩形)是在代码隐藏中手动添加的,如果有影响的话。

用户控制代码:

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++;
                }
            }
        }
    }
}

【问题讨论】:

  • 您可以发布您的UserControl 代码吗?我尝试按照您的描述自己创建一个,但无法复制问题
  • 是的,我在同一条船上。我在几个项目中多次这样做,并且内容始终可见,因为 Usercontrol 的背景始终位于其子项的底部。
  • 刚刚看到您的编辑。是的,这可能会有所作为。如果您正在绘制到 UserControl。我的建议是在你的用户控件上放置一个画布并在上面绘制。
  • @TzahMama 添加了控件的代码。
  • @RobertSnyder 会试试的。

标签: c# wpf


【解决方案1】:

为了编写一个通过重写的OnRender 方法进行渲染的自定义控件,您不应该从UserControl 甚至Control 派生,因为它们通过ControlTemplate 绘制自己,这也填充他们的区域用Background 刷子。所有这些都是在他们的 OnRender 方法之外完成的,因此覆盖它而不调用基类的 OnRender 将无济于事。

相反,从FrameworkElementUIElement 派生,声明Background 属性并在执行其余渲染之前用背景填充控制区域:

public class CustomControl : FrameworkElement
{
    public static readonly DependencyProperty BackgroundProperty =
        Control.BackgroundProperty.AddOwner(typeof(CustomControl));

    public Brush Background
    {
        get { return (Brush)GetValue(BackgroundProperty); }
        set { SetValue(BackgroundProperty, value); }
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext); // just good practice
        drawingContext.DrawRectangle(Background, null, new Rect(RenderSize));

        // your rendering code goes here...
    }
}

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

【讨论】:

  • +1 有效!谢谢你的回答和教育。由于 FrameworkElement 不支持 OnMouseDoubleClick(),我会找到解决方法。另一个问题:为什么不调用 base.OnRender() 可以?
  • 基本实现什么都不做。但是,调用它并没有什么坏处。无论如何,这是一种很好的做法。
【解决方案2】:

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

<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>

然后我根据传入的数据设置它的宽度和高度。使列等于数据的计数。 (请注意,我没有像您那样输入更改图形颜色的逻辑)。然后我添加了一个边框(它有一个边框和一个背景)并将其添加到 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);
    }

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

【讨论】:

  • +1 谢谢。但是,我的代码使用 DrawRectangle() 调用而不是形状,所以我不能使用你的方法。
  • @Sabuncu 好的,所以用 Canvas 替换 UniformGrid,然后在画布上绘图。您不能在用户控件上绘图,因为背景会覆盖您在用户控件上绘制的内容
【解决方案3】:

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

此模板中的默认设置会导致它从 ControlTemplate 复制边框的背景,这会导致背景(莫名其妙地呈现在您的自定义控件之上)过度绘制您的控件。

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-18
    • 1970-01-01
    • 1970-01-01
    • 2018-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多