Интерактивная карта на Avalonia

Рейтинг: 0Ответов: 1Опубликовано: 05.02.2023

Мне нужно сделать интерактивную карту города. Некоторые здания - просто фон. Некоторые здания интерактивны, они запускают событие, когда пользователь нажимает на них.

Проблема заключается в поддержке изменения размера пользовательского интерфейса. Интерактивные здания должны изменять свой размер вместе со всей картой и не менять своего местоположения относительно самой карты.

Я попробовал использовать Canvas. Карта отображается с помощью Image. Интерактивные здания - это кнопки с Image в качестве контента. К сожалению, при такой реализации мне приходится вычислять размеры и положения всех изображений в моем коде и изменять их при изменении размера окна приложения.

Как вы думаете, есть ли более удобное решение?

<UserControl <...>
         Bounds="{Binding CanvasBounds, Mode=OneWayToSource}">

<Canvas VerticalAlignment="Stretch"
        HorizontalAlignment="Stretch"
        ClipToBounds="True">
    
    <!-- This is city map image -->
    <Image Canvas.Left="0"
           Canvas.Top="0"
           ZIndex="1"
           Stretch="Uniform"
           HorizontalAlignment="Stretch"
           VerticalAlignment="Stretch"
           IsEnabled="False"
           Width="{Binding MapImageWidth, Mode=OneWay}"
           Height="{Binding MapImageHeight, Mode=OneWay}"
           Source="avares://App/Assets/Images/Map0/map.png"/>
    
    <!-- This is button of interactive building -->
    <Button Canvas.Left="72"
            Canvas.Top="124"
            Classes="GameMapButton">
        <Image Source="avares://App/Assets/Images/Map0/big_office_0_normal.png"/>
        
        <Button.Styles>
            <Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
                <Setter Property="Content">
                    <Template>
                        <Image Source="avares://App/Assets/Images/Map0/big_office_0_pointerover.png"/>
                    </Template>
                </Setter>
            </Style>
        </Button.Styles>
        
        <Button.Styles>
            <Style Selector="Button:pressed /template/ ContentPresenter#PART_ContentPresenter">
                <Setter Property="Content">
                    <Template>
                        <Image Source="avares://App/Assets/Images/Map0/big_office_0_pressed.png"/>
                    </Template>
                </Setter>
            </Style>
        </Button.Styles>
    </Button>
    
    <...>
</Canvas>

Ответы

▲ 0

Попробовал использовать Viewbox в корне UserControl. Поскольку Canvas таким образом отображаться не хотел, заменил его на обычную Panel. Расположение кнопок задал через Margin. Пришлось все смещения находить вручную, после каждого изменения перезапуская приложение. Это было очень утомительно.

Решение вроде бы работает. При изменении размеров окна приложения кнопки не смещаются относительно карты и меняют размер пропорционально.

<UserControl ...>
<Viewbox Stretch="Uniform">
    <Panel>
        <Image ZIndex="1"
               IsEnabled="False"
               Source="avares://App/Assets/Images/Map0/map.png" />

        <Button Margin="73 -210 0 0"
                Classes="GameMapButton">
            <Image Source="avares://App/Assets/Images/Map0/big_office_0_normal.png" />

            <Button.Styles>
                <Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
                    <Setter Property="Content">
                        <Template>
                            <Image
                                Source="avares://App/Assets/Images/Map0/big_office_0_pointerover.png" />
                        </Template>
                    </Setter>
                </Style>
            </Button.Styles>

            <Button.Styles>
                <Style Selector="Button:pressed /template/ ContentPresenter#PART_ContentPresenter">
                    <Setter Property="Content">
                        <Template>
                            <Image Source="avares://App/Assets/Images/Map0/big_office_0_pressed.png" />
                        </Template>
                    </Setter>
                </Style>
            </Button.Styles>
        </Button>

        <Button Margin="720 -285 0 0"
                Classes="GameMapButton">
            <Image Source="avares://App/Assets/Images/Map0/middle_office_0_normal.png" />

            <Button.Styles>
                <Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
                    <Setter Property="Content">
                        <Template>
                            <Image
                                Source="avares://App/Assets/Images/Map0/middle_office_0_pointerover.png" />
                        </Template>
                    </Setter>
                </Style>
            </Button.Styles>

            <Button.Styles>
                <Style Selector="Button:pressed /template/ ContentPresenter#PART_ContentPresenter">
                    <Setter Property="Content">
                        <Template>
                            <Image
                                Source="avares://App/Assets/Images/Map0/middle_office_0_pressed.png" />
                        </Template>
                    </Setter>
                </Style>
            </Button.Styles>
        </Button>

        <...>
    </Panel>
</Viewbox>

По ощущениям от написания кода, решение тупое. Я буду рад альтернативным вариантам.