代码之家  ›  专栏  ›  技术社区  ›  scobi

是否可能使给定区域(而不是整个WPF控件)上的Visual()无效?

  •  7
  • scobi  · 技术社区  · 15 年前

    我有一个复杂的WPF控件,它在OnRender中绘制了很多原语(有点像地图)。当其中一小部分发生更改时,我只想为受影响的元素重新发出render命令,而不是运行整个onrender。虽然我对onrender函数在调整大小或其他方面的性能很满意,但对于基于鼠标悬停的基本体突出显示,它的速度不够快。

    目前,我知道如何强制屏幕更新的唯一方法是调用invalidVisual()。无法发送脏的rect区域以使其无效。

    wpf屏幕组成的最小粒度是ui元素吗?我需要将原语渲染成一个中间目标,然后让它使用invalidVisual()更新到屏幕上吗?

    3 回复  |  直到 8 年前
        1
  •  3
  •   Simon Mourier    14 年前

    当您想要编写WPF自定义/复合控件时,应该尽量避免重写OnRender,尤其是当您计划使其部分无效时。使用addVisualChild+override VisualChildrenCount+override GetVisualChild+override Measure&arranged(带2个子级的伪代码)更容易:

    private void BuildMyControls()
    {
      AddVisualChild(subControl1);
      AddVisualChild(subControl2);
    }
    
    protected override int VisualChildrenCount
    {
      get
      {
        return 2;
      }
    }
    
    protected override Visual GetVisualChild(int index)
    {
      if (index == 0) return subControl1;
      if (index == 1) return subControl2;
      return null; // should never be called in fact...
    }
    
    protected override Size MeasureCore(Size availableSize)
    {
      base.Measure...
      BuildMyControls();
      .. measure them, probably call subControlX.Measure(...);
    }
    
    protected override void ArrangeCore(Rect finalRect)
    {
      base.ArrangeCore(finalRect);
      ... arrange them, probably call subControlX.Arrange
    }
    

    有了这种代码,您就可以使一部分无效,比如subcontrolx.invalidXXX();

        2
  •  0
  •   Steven    15 年前

    WPF的工作方式不是这样的,所以不能使区域无效。但是,可以进行一些优化。有一个度量、排列和渲染过程。如果控件移动,但实际呈现的内容不变,则可以告诉WPF只执行排列过程。您可以使用frameworkpropertiesmetadata和frameworkpropertiesmetadaatooptions触发依赖项属性值更改的这些无效性。( http://msdn.microsoft.com/en-us/library/system.windows.frameworkpropertymetadataoptions.aspx )

        3
  •  0
  •   David Jeske    8 年前

    你不应该使用 InvalidateVisual() 除非控件的大小发生变化,否则会导致相当昂贵的UI重新布局。

    WPF是一个 保留 绘图系统。这意味着 OnRender() 最好打电话来 AccumulateDrawingObjects() . 它实际上是在积累一个活动图形对象树,每个布局只需要发生一次。然后,只要需要,它就使用这些对象来绘制用户界面。若要在不重新布局的情况下更改UI部分的外观,可以在以下时间之后更新某些对象(如DrawingGroup、RenderTargetBitmap和WriteableBitmap) OnRender()时 ,只要你愿意。

    若要稍后更新UI的一部分,请将这些命令包装在 DrawingGroup 然后放 那个 对象进入 DrawingContext . 那么你可以 Open() 并随时更新它,WPF将自动重新绘制该部分的UI。

    这就是它的样子:

    DrawingGroup backingStore = new DrawingGroup();
    
    protected override void OnRender(DrawingContext drawingContext) {      
        base.OnRender(drawingContext);            
    
        Render(); // put content into our backingStore
        drawingContext.DrawDrawing(backingStore);
    }
    
    // I can call this anytime, and it'll update my visual drawing
    // without ever triggering layout or OnRender()
    private void Render() {            
        var drawingContext = backingStore.Open();
        Render(drawingContext);
        drawingContext.Close();            
    }