【问题标题】:How to Rearrange rectangles on canvas after modifying height of any rectangle in wpf?修改wpf中任何矩形的高度后如何在画布上重新排列矩形?
【发布时间】:2013-07-15 12:28:43
【问题描述】:

我正在从用户直接在网格行中输入的网格单元格值中添加Rectangle。当我修改特定列的值时说ThicknessHeight 然后它会增加所选行矩形的Height 但它不会在所选行矩形之后重新排列它下面的所有矩形。

在 xaml.cs 中

public class MyLayer : INotifyPropertyChanged 
{

    public string Thickness { get; set; }
    public string OffsetRight { get; set; }
    public string OffsetLeft { get; set; }
    public string Material { get; set; }
    public string MaterialPopup { get; set; }
    public Rectangle rectangle { get; set; }

    public GlassRectangle GlassRectangle { get; set; }
    public MaterialLayer()
    {
        GlassRectangle = new GlassRectangle();

    }
    event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
    {
        add { }
        remove {  }
    }
}

public class GlassRectangle
{

    public Rectangle Rectangle { get; set; }
    public double Top = 0;
    public GlassRectangle()
    {
        Rectangle = new Rectangle();

    }
}

private void gridInner_CellValueChanged(object sender, DevExpress.Xpf.Grid.CellValueChangedEventArgs e)
        {
            string cellValue = string.Empty;
            MyLayer currentLayer = ((MyLayer)(e.Row));

            if (e.Column.HeaderCaption.ToString() == "Thickness")
            {

                cellValue =(e.Value.ToString());
                //there is alredy a rectangle - means this is edit mode
                if (currentLayer.rectangle != null)
                {
                    currentLayer.rectangle.Height = Convert.ToDouble(cellValue);
                    currentLayer.rectangle.Stroke = new SolidColorBrush(Color.FromRgb(0, 255, 0));

                }
                //else this is insert mode
                else
                {

                    currentLayer.rectangle = CreateRectangle(cellValue);
                }

            }

        }

 protected Rectangle  CreateRectangle(string cellval)
        {
            Rectangle newrect = new Rectangle();
            newrect.Stroke = Brushes.Red;
            newrect.StrokeThickness = 1;
            if (cellval.ToString().Contains("."))
            {
                newrect.Height = Convert.ToDouble(cellval) * 100;
            }
            else
            {
                newrect.Height = Convert.ToDouble(cellval);
            }
            newrect.Width = width;
            Canvas.SetLeft(newrect, 100);
            double canvasTop = 0.0;
            if (canvasboard.Children.Count > 0)
            {
                var lastChildIndex = canvasboard.Children.Count - 1;
                var lastChild = canvasboard.Children[lastChildIndex] as FrameworkElement;
                if (lastChild != null)
                    //lastChild.Height-1: so that it come extactly on existing if set to +1 it comes below first rectangle
                    canvasTop = Canvas.GetTop(lastChild) + lastChild.Height - 1;
            }

            Canvas.SetTop(newrect, canvasTop);
            val = val + 1;
            newrect.Tag = val;
            canvasboard.Children.Add(newrect);
            //rectangle = rect;

            foreach (UIElement ui in canvasboard.Children)
            {
                if (ui.GetType() == typeof(Rectangle))
                {
                    itemstoremove.Add(ui);
                }
            }

            return newrect;
        }

新事件方法:

private void gridMaterialInner_CellValueChanged(object sender, DevExpress.Xpf.Grid.CellValueChangedEventArgs e)
        {
            string cellValue = string.Empty;
            string cellOldValue = string.Empty;
            MyLayer currentLayer = ((MyLayer)(e.Row));
            if (e.Column.HeaderCaption.ToString() == "Thickness")
            {
                //current cell value
                cellValue =(e.Value.ToString());// GetRowCellValue(e.RowHandle, gridMaterialInner.Columns["LastName"]).ToString();
                //there is alredy a rectangle - means this is edit mode
                double currentheight = 0.0;
                double oldht = 0.0;
                // old cell value
                if (e.OldValue != null)
                {
                    cellOldValue = (e.OldValue.ToString());
                }
                if (currentLayer.rectangle != null)
                {
                    if (cellValue.ToString().Contains("."))
                    {
                        currentheight = Convert.ToDouble(cellValue) * 100;
                    }
                    else
                    {
                        currentheight = Convert.ToDouble(cellValue) * 100;
                    }
                    if (cellOldValue.ToString().Contains("."))
                    {
                        oldht = Convert.ToDouble(cellOldValue) * 100;
                    }
                    else if(cellOldValue!=string.Empty)
                    {
                        oldht = Convert.ToDouble(cellOldValue) * 100;
                    }

                    currentLayer.rectangle.Height = currentheight;
                    currentLayer.rectangle.Stroke = new SolidColorBrush(Color.FromRgb(0, 255, 0));

                    //Refresh();
                    //Get the index of selected row
                    int layerIndex = materialBindlist.IndexOf(currentLayer);
                    for(int i = layerIndex; i < materialBindlist.Count-1; i++)
                    {
                        //set the top position of all other rectangles that are below selected rectangle/row
                        //(Current-Old)+Top
                        Canvas.SetTop(materialBindlist[i + 1].rectangle, (currentheight - oldht) + materialBindlist[i + 1].GlassRectangle.Top);
                        //Canvas.SetTop(materialBindlist[i].rectangle, (currentheight - oldht) + materialBindlist[i + 1].GlassRectangle.Top);

                    }
                }
                //else this is insert mode
                else
                {
                    //MaterialLayer object
                    currentLayer.rectangle = CreateRectangle(cellValue);
                    //store Top & Rectangle object in GlassRectangle class which is referenced in MaterialLayer class
                    currentLayer.GlassRectangle.Rectangle = currentLayer.rectangle;
                    currentLayer.GlassRectangle.Top = canvasTop;
                }

            }

        }

这会像画布上的堆叠项目一样创建一个接一个的矩形。但是当我修改Thickness 列的值时,它是HeightHeight,它反映在画布上,但下面的其他矩形必须出现在当前矩形的更改高度之后。

注意:我不能在我的应用程序中使用 WrapPanel。只是为了使用 Canvas 修改现有代码。

帮助赞赏!

在 CellChange 事件中修改了 For 循环:

int layerIndex = materialBindlist.IndexOf(currentLayer);
                    for(int i = layerIndex; i < materialBindlist.Count-1; i++)
                    {
                        //set the top position of all other rectangles that are below selected rectangle/row
                        //(Current-Old)+Top
                        double top=Convert.ToDouble((currentHeight - oldHeight) + materialBindlist[i + 1].GlassRectangle.Top);
                        Canvas.SetTop(materialBindlist[i + 1].rectangle,top);

                        materialBindlist[i + 1].GlassRectangle.Top = top;



                    }

【问题讨论】:

  • 转储 Canvas 并编写一个自定义面板,覆盖 Measure 和 Arrange 。

标签: c# wpf canvas drawrectangle


【解决方案1】:

即使使用Canvas 也可以完成您要查找的内容,但是您应该真正考虑为此使用ItemsControl 之类的东西。

强制使用Canvas时的解决方法:

private void Refresh() {
  for (int i = 1; i < canvasboard.Children.Count; ++i) {
    var currentElement = canvasboard.Children[i] as FrameworkElement;
    var previousElement = canvasboard.Children[i - 1] as FrameworkElement;
    if (currentElement == null || previousElement == null)
      return;
    var requiredTop = Canvas.GetTop(previousElement) + previousElement.Height - 1;
    if (Math.Abs(Canvas.GetTop(currentElement) - requiredTop) > 0.0)
      Canvas.SetTop(currentElement, requiredTop);
  }
}

现在“在”您更改Canvas 中现有元素的大小之后调用此函数,它将相应地重新定位元素以适应新维度。在您的代码中,在“编辑”模式下设置新高度后,它将从 gridInner_CellValueChanged(...) 函数中调用。

你应该尝试做什么:

如果你能够说服任何你需要的人并开始使用像ItemsControl 这样的东西,这将会简单得多。

举个粗略的例子:

xaml 可能是:

<ItemsControl ItemsSource="{Binding Items}">
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <VirtualizingStackPanel Orientation="Vertical" />
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
</ItemsControl>

在代码中将Items 声明为public ObservableCollection&lt;Rectangle&gt; Items { get; set; }

现在您的 Add() 函数可能是:

private void Add() {
  var rect = new Rectangle {
    Stroke = Brushes.Red,
    StrokeThickness = 1,
    Height = Convert.ToDouble(txtheight.Text),
    Width = 100
  };
  Items.Add(rect);
}

以及当您编辑现有控件时的更新,在这种情况下将是自动的。没有硬编码的定位,因为布局容器会为您处理所有这些混乱。

您当然可以将 Items 集合类型切换为您自己的自定义控件类型 MyLayer 并通过它实现 INPC,更改仍将继续是自动的。您现在必须定义一个 DataTemplate 才能渲染您的项目,但这就像 xaml 中的 3 行工作。

当需要调整现有控件时,您也可以直接使用 Items 属性,而不必在代码隐藏中引用 ItemsControl。绑定应该自动处理对视图的更新。

【讨论】:

  • 嗨@Viv 感谢您的回复!但是此代码仅在我增加现有大小时才有效,但是当我再次减小大小时,它不会重新排列..!有什么想法吗?
  • @SHEKHARSHETE 是的,这是我的错,我只检查前一个项目的扩展而不是收缩。我已经更新了答案中的功能。试试看,应该没问题。
  • 嘿@Viv,你摇滚!它工作正常..!非常感谢..! :) 我真的很感谢你的工作!
  • 你好@Viv,我已经添加了“新方法”代码,请你帮我了解一下我用来设置以下矩形自动调整的for循环。这是因为我现在才知道画布也可能包含其他图形,所以我添加了 Rectangle 参考。
  • @SHEKHARSHETE 嘿,很抱歉,我没有听懂你的问题。不太确定你要我寻找什么帮助。我可以看到您已经编辑并添加了新功能?你到底在问我什么?您想知道从哪里调用Refresh() 函数还是该函数有问题?
【解决方案2】:

在 Cell Change 事件中修改了 for 循环:

int layerIndex = materialBindlist.IndexOf(currentLayer);
                    for(int i = layerIndex; i < materialBindlist.Count-1; i++)
                    {
                        //set the top position of all other rectangles that are below selected rectangle/row
                        //(Current-Old)+Top
                        double top=Convert.ToDouble((currentHeight - oldHeight) + materialBindlist[i + 1].GlassRectangle.Top);
                        Canvas.SetTop(materialBindlist[i + 1].rectangle,top);

                        materialBindlist[i + 1].GlassRectangle.Top = top;



                    }

现在可以使用了..!谢谢大家!

【讨论】:

    猜你喜欢
    • 2013-01-24
    • 2014-02-08
    • 2022-11-12
    • 2015-03-16
    • 1970-01-01
    • 2018-03-20
    • 2018-09-27
    • 1970-01-01
    • 2016-07-01
    相关资源
    最近更新 更多