WPF 自定义框架元素/IScrollInfo

发布于 2024-10-19 15:46:20 字数 3919 浏览 6 评论 0原文

我有一个简单的测试应用程序,带有基本的自定义 FrameworkElement 实现(下面的 TestElement)。 TestElement 创建了一些绘图视觉效果,并在构造函数中绘制了一些宽度为 600 的内容。它还实现了 IScrollinfo 的必要位;包含该元素的窗口有一个滚动查看器,最大尺寸为 300x300。出现滚动条,但不滚动 TestElement 的内容。

任何人都可以建议我想做的事情是否可能,如果可以的话我做错了什么。我可以在 SetHorizo​​ntalOffset 中重新渲染绘图视觉效果,但出于性能原因不想这样做,因为我已经绘制了所需的所有内容。

我希望这个问题有意义 - 如果没有,请告诉我,我可以澄清。

非常感谢 - Karl

public class TestElement : FrameworkElement, IScrollInfo
{
    DrawingVisual visual;
    DrawingVisual visual2;
    public TestElement()
    {
        Draw();
        this.MaxWidth = 600;
        this.MaxHeight = 300;
    }
    public void Draw()
    {
        if(visual == null)
        {
            visual = new DrawingVisual();
            base.AddVisualChild(visual);
            base.AddLogicalChild(visual);
        }
        if (visual2 == null)
        {
            visual2 = new DrawingVisual();
            base.AddVisualChild(visual2);
            base.AddLogicalChild(visual2);
        }
        Random rand = new Random();
        var pen = new Pen(Brushes.Black, 1);
        using(var dc = visual.RenderOpen())
        {
            for (int i = 0; i < 400; i++) 
            {
                var r = rand.Next(10, 200);
                dc.DrawLine(pen, new Point(i, r), new Point(i, 0));
            }
        }
        using (var dc = visual2.RenderOpen())
        {
            for (int i = 0; i < 200; i++)
            {
                var r = rand.Next(10, 200);
                dc.DrawLine(pen, new Point(i, r), new Point(i, 0));
            }
            visual2.Offset = new Vector(400, 0);
        }
    }
    protected override int VisualChildrenCount 
    {
        get { return 2; }
    }
    protected override Visual GetVisualChild(int index)
    {
        return index == 0 ? visual : visual2;
    }
    protected override Size MeasureOverride(Size availableSize)
    {
        viewport = availableSize;
        owner.InvalidateScrollInfo();
        return base.MeasureOverride(availableSize);
    }
    protected override Size ArrangeOverride(Size finalSize)
    {
        var value = base.ArrangeOverride(finalSize);
        return base.ArrangeOverride(finalSize);
    }


    Point offset = new Point(0,0);
    public void SetHorizontalOffset(double offset)
    {
        this.offset.X = offset;
        this.InvalidateArrange();
    }

    public void SetVerticalOffset(double offset)
    {
        this.offset.Y = offset;
    }

    public Rect MakeVisible(Visual visual, Rect rectangle)
    {
        throw new NotImplementedException();
    }

    public bool CanVerticallyScroll { get; set; }
    public bool CanHorizontallyScroll { get; set; }

    Size extent = new Size(600, 300);

    private Size viewport = new Size(0, 0);
    public double ExtentWidth
    {
        get { return extent.Width; }
    }

    public double ExtentHeight
    {
        get {return extent.Height; }
    }

    public double ViewportWidth
    {
        get { return viewport.Width; }
    }

    public double ViewportHeight
    {
        get { return viewport.Height; }
    }

    public double HorizontalOffset
    {
        get { return offset.X; }
    }

    public double VerticalOffset
    {
        get { return offset.Y; }
    }

    private ScrollViewer owner;

    public ScrollViewer ScrollOwner
    {
        get { return owner; }
        set { owner = value; }
    }


}

the xaml:

<Window x:Class="TestWpfApp.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l ="clr-namespace:TestWpfApp"
Title="TestWpfApp" Height="300" Width="300" >
<Grid>
    <ScrollViewer CanContentScroll="True" HorizontalScrollBarVisibility="Visible">
        <l:TestElement CanHorizontallyScroll="True" />
    </ScrollViewer>
</Grid>

I've got a simple test app with a basic custom FrameworkElement implementation (TestElement below). The TestElement creates a couple of drawing visuals and draws some stuff in the constructor to a width of 600. It also implements the necessary bits of IScrollinfo; The Window containing the element has got a scrollviewer and a max size of 300x300. The scrollbar appears but does not scroll the content of the TestElement.

Can anyone suggest whether what I am trying to do is possible and if so what I am doing wrong. I could re-render the drawing visuals in SetHorizontalOffset but don't want to for performance reasons as I have already drawn all I need to.

I hope the question makes some sense - let me know if not and I can clarify.

Many thanks - Karl

public class TestElement : FrameworkElement, IScrollInfo
{
    DrawingVisual visual;
    DrawingVisual visual2;
    public TestElement()
    {
        Draw();
        this.MaxWidth = 600;
        this.MaxHeight = 300;
    }
    public void Draw()
    {
        if(visual == null)
        {
            visual = new DrawingVisual();
            base.AddVisualChild(visual);
            base.AddLogicalChild(visual);
        }
        if (visual2 == null)
        {
            visual2 = new DrawingVisual();
            base.AddVisualChild(visual2);
            base.AddLogicalChild(visual2);
        }
        Random rand = new Random();
        var pen = new Pen(Brushes.Black, 1);
        using(var dc = visual.RenderOpen())
        {
            for (int i = 0; i < 400; i++) 
            {
                var r = rand.Next(10, 200);
                dc.DrawLine(pen, new Point(i, r), new Point(i, 0));
            }
        }
        using (var dc = visual2.RenderOpen())
        {
            for (int i = 0; i < 200; i++)
            {
                var r = rand.Next(10, 200);
                dc.DrawLine(pen, new Point(i, r), new Point(i, 0));
            }
            visual2.Offset = new Vector(400, 0);
        }
    }
    protected override int VisualChildrenCount 
    {
        get { return 2; }
    }
    protected override Visual GetVisualChild(int index)
    {
        return index == 0 ? visual : visual2;
    }
    protected override Size MeasureOverride(Size availableSize)
    {
        viewport = availableSize;
        owner.InvalidateScrollInfo();
        return base.MeasureOverride(availableSize);
    }
    protected override Size ArrangeOverride(Size finalSize)
    {
        var value = base.ArrangeOverride(finalSize);
        return base.ArrangeOverride(finalSize);
    }


    Point offset = new Point(0,0);
    public void SetHorizontalOffset(double offset)
    {
        this.offset.X = offset;
        this.InvalidateArrange();
    }

    public void SetVerticalOffset(double offset)
    {
        this.offset.Y = offset;
    }

    public Rect MakeVisible(Visual visual, Rect rectangle)
    {
        throw new NotImplementedException();
    }

    public bool CanVerticallyScroll { get; set; }
    public bool CanHorizontallyScroll { get; set; }

    Size extent = new Size(600, 300);

    private Size viewport = new Size(0, 0);
    public double ExtentWidth
    {
        get { return extent.Width; }
    }

    public double ExtentHeight
    {
        get {return extent.Height; }
    }

    public double ViewportWidth
    {
        get { return viewport.Width; }
    }

    public double ViewportHeight
    {
        get { return viewport.Height; }
    }

    public double HorizontalOffset
    {
        get { return offset.X; }
    }

    public double VerticalOffset
    {
        get { return offset.Y; }
    }

    private ScrollViewer owner;

    public ScrollViewer ScrollOwner
    {
        get { return owner; }
        set { owner = value; }
    }


}

the xaml:

<Window x:Class="TestWpfApp.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l ="clr-namespace:TestWpfApp"
Title="TestWpfApp" Height="300" Width="300" >
<Grid>
    <ScrollViewer CanContentScroll="True" HorizontalScrollBarVisibility="Visible">
        <l:TestElement CanHorizontallyScroll="True" />
    </ScrollViewer>
</Grid>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

醉生梦死 2024-10-26 15:46:20

刚刚喝了一杯啤酒,这有助于找到解决方案。 ;-)

这不是最终的“一切都做得很好”的解决方案,但它肯定会进一步帮助您:

ArrangeOverride 的实现中,尝试:

protected override Size ArrangeOverride(Size finalSize)
{
    this.visual.Offset = new Vector(-this.HorizontalOffset, -this.VerticalOffset);

    var value = base.ArrangeOverride(finalSize);
    return base.ArrangeOverride(finalSize);
}

基本上您必须自己移动对象。

有关详细信息,请参阅这篇文章:iscrollinfo 教程

通常,您必须使用变换将对象移动到滚动它们的位置。

Just had a beer too and it helps to find a solution. ;-)

It's not the ultimative "all well done" solution, but for sure it will help you further:

In your implementation of ArrangeOverride, try:

protected override Size ArrangeOverride(Size finalSize)
{
    this.visual.Offset = new Vector(-this.HorizontalOffset, -this.VerticalOffset);

    var value = base.ArrangeOverride(finalSize);
    return base.ArrangeOverride(finalSize);
}

Basically you have to move your objects yourself.

For more information see this article too: iscrollinfo tutorial
.
Normally you would have to use Transformations to move the objects there where you scrolled them.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文