开发者

WPF Canvas and Grid overlay

I have a Grid which represents som开发者_如何学Goe data, and I need a Canvas to overlay on top of it to layout some lines. The Canvas is inside it's own UserControl.

The problem is that the Canvas and its contents should autoresize when the Grid resizes width and height.

I added the Canvas inside a ViewBox, but it didn't do the trick. When the Grid resizes, the Canvas doesn't. The purpose of the Canvas is to overlay a ruler-like functionality on top of the grid.

Looking forward for your solutions.

EDIT

I cannot use a style on the grid to replace the canvas, because the grid is showing different information than the canvas does. Think of it as chart, in which there are bar columns of different sizes (in my case the grid) and the days are lines in an overlay (just as a Gannt Chart)

My code:

    taxCanvas = new TimeAxis();
    Grid.SetRowSpan(taxCanvas, GRightMain.RowDefinitions.Count);
    Grid.SetColumnSpan(taxCanvas, GRightMain.ColumnDefinitions.Count);

    Grid.SetColumn(taxCanvas, 0);
    Grid.SetRow(taxCanvas, 0);


    Grid.SetZIndex(taxCanvas, -1);

    taxCanvas.Height = GRight.ActualHeight;
    taxCanvas.Width = GRight.ActualWidth;

    GRightMain.Children.Add(taxCanvas);

TimeAxis is my canvas usercontrol, GRightMain is a grid which holds both my canvas and the grid with the content (Gright) in the same row and column.

Hope this helps


A canvas is definitely the wrong approach in my opinion.

I strongly recommend looking up Adorners. You could create a custom adorner to do exactly this.

An Adorner is basically a "non-interactive window" that sits on top of all UIElements. It allows you to do anything you'd like (create controls, draw things, etc...) that will appear on top of the control itself.

Picture a wooden coffee table with a piece of clear glass on top of it. If you draw on the clear glass, you still see through to the coffee table. The only difference would be that you can actually reach straight through clear glass on the coffee table and touch the wood itself.

I hate posting MSDN links...but...eh. In this case it would be a good start:

http://msdn.microsoft.com/en-us/library/ms743737.aspx

EDIT:

I threw something together quickly. Hopefully this helps?

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:loc="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
  <Grid>
     <loc:GridWithRulerxaml></loc:GridWithRulerxaml>
     <Button Height="20" Width="50" >Click me</Button>
     <TextBox Width="150" Height="25" HorizontalAlignment="Left">This is a text box</TextBox>
  </Grid>
</Window>

USERCONTROL:

<UserControl x:Class="WpfApplication1.GridWithRulerxaml"
             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">
  <Grid>

  </Grid>
</UserControl>

THE USERCONTROL CODE-BEHIND:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;

namespace WpfApplication1
{
  /// <summary>
  /// Interaction logic for GridWithRulerxaml.xaml
  /// </summary>
  public partial class GridWithRulerxaml : UserControl
  {
    public GridWithRulerxaml()
    {
      InitializeComponent();

      //Loaded event is necessary as Adorner is null until control is shown.
      Loaded += GridWithRulerxaml_Loaded;

    }

    void GridWithRulerxaml_Loaded(object sender, RoutedEventArgs e)
    {
      var adornerLayer = AdornerLayer.GetAdornerLayer(this);
      var rulerAdorner = new RulerAdorner(this);
      adornerLayer.Add(rulerAdorner);
    }
  }
}

FINALLY THE ADORNER ITSELF:

using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;

namespace WpfApplication1
{
  public class RulerAdorner : Adorner
  {
    private FrameworkElement element;
    public RulerAdorner(UIElement el) : base(el)
    {
      element = el as FrameworkElement;
    }

    protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
    {
      base.OnRender(drawingContext);

      double height = element.ActualHeight;
      double width = element.ActualWidth;

      double linesHorizontal = height/50;
      double linesVertical = width/50;

      var pen = new Pen(Brushes.RoyalBlue, 2) { StartLineCap = PenLineCap.Triangle, EndLineCap = PenLineCap.Triangle };

      int offset = 0;

      for (int i = 0; i <= linesVertical; ++i)
      {
        offset = offset + 50;
        drawingContext.DrawLine(pen, new Point(offset, 0), new Point(offset, height));
      }

      offset = 0;

      for (int i = 0; i <= linesHorizontal; ++i)
      {
        offset = offset + 50;
        drawingContext.DrawLine(pen, new Point(0, offset), new Point(width, offset));
      }
    }
  }
}

If you would like me to elaborate more on the code itself let me know.

I confirmed that this will draw a grid over top of whatever is on your main page. You should still be able to interact with what's beneath.


What is it that your are trying to do? If you just want to add gridlines to the whole grid you could do it by setting styles like this. Alternatively you could use Adorners. They are there to decorate/adorn elements on a separate layer. The good thing about adorners is, that allthough they are on a separate layer they keep in sync (size, position, transformation) with the element they adorn.


You could use binding to "bind" the size of the Canvas and the size of the Grid to each other. So when the Grid resizes, the Canvas will do too automatically.

Also you could use a Converter for the binding of you have to calculate and offset like a distance from the border.

Binding in WPF is always a good idea so make sure you know the basics of how it works.

Anyway you could always do it in the code-Behind (C#/VB) in the Size-Event. Just determine the size of one control and set it to another. Like getting the actualHeight property of the Grid and set it to the height property of the canvas.

Maybe this will give you an idea: http://blogs.msdn.com/bencon/archive/2006/05/10/594886.aspx or http://www.switchonthecode.com/tutorials/wpf-tutorial-binding-converters

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜