As comments suggested, i've derived my class from ScrollableControl
and used AutoScroll
property to make scrolling logic into my control.
Sadly Microsoft documentation about ScrollableControl.AutoScrollMinSize
lacks details about what this property actually mean. Neither from name nor from description.
It says:
A Size that determines the minimum size of the virtual area through which the user can scroll.
And i couldn't figure out for some time what does this actually mean, only after, i came up with imo better description: A size of a content trough which the user can scroll.
It also says nowhere that for scrollbars to appear this minimal size should be bigger than control itself. And so, i could finally implement what i needed:
Here is the code, that sets AutoScrollMinSize
this.AutoScrollMinSize = new Size(0, _displayedItems.Count * (this.ItemHeight + this.ItemsMargin));
this.VerticalScroll.LargeChange = this.ItemHeight;
this.VerticalScroll.SmallChange = this.ItemHeight / 3;
For X component i have 0, because i don't need horizontal scrolling.
For Y component i calculate the size of entire content that user can interact with.
And for actual scrolling of a content, i just translate graphics for scroll value:
e.Graphics.TranslateTransform(0, this.AutoScrollPosition.Y);
And it works just as intended.

Hope it help anyone who stumbles on this exact same problem as i did.