Как программно привязать к свойству переданного объекта

Я пытался связать свойства источника, строки и столбца вновь сгенерированного изображения с некоторым значением объекта в моей модели представления. В идеале это можно было бы решить путем привязки к свойствам объекта, переданным в eventArgs. Есть ли способ сделать это?

Вот пример того, что я хочу сделать:

private void OnNewEnemy(EnemyViewModel enemyViewModel)
        {
            Image Enemy = new Image()
            {
                Stretch = Stretch.Fill
            };

            Enemy.SetBinding(Image.SourceProperty, new Binding("enemyViewModel.Icon"));
            Enemy.SetBinding(Grid.RowProperty, new Binding("enemyViewModel.Row"));
            Enemy.SetBinding(Grid.ColumnProperty, new Binding("enemyViewModel.Column"));

            locationGrid.Children.Add(Enemy);
        }

Я изучал использование синтаксиса new Binding("property.subproperty"), но, поскольку мои объекты "enemyViewModel" регулярно добавляются и удаляются из списка в моей модели представления, этот метод, похоже, не работает.

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

Спасибо за любые предложения.

«Я также знаю, что писать в коде позади — плохая практика» — по мнению кого?

Ryan Wilson 13.02.2023 14:31

Я слышал, что этого лучше избегать, если это действительно необходимо

AuthorBrotherGames 13.02.2023 14:48

Вы можете передать контекст данных изображения объекту enemyViewModel, используя свойство DataContext. Таким образом, вы можете напрямую привязать свойства элемента управления Image к свойствам объекта EnemyViewModel.

Nicolas Régnier 13.02.2023 14:59

Если есть несколько врагов, которые нужно отобразить, то я бы подошел к этому с помощью шаблонов моделей просмотра в панели элементов элемента управления.

Andy 13.02.2023 15:12

Я не знаю, над каким программным обеспечением работают люди, которые рекомендуют программный код. Причина, по которой код минимизируется или его избегают в коммерческих командах, заключается в том, что сложно писать автоматические тесты на основе пользовательского интерфейса. Логика пользовательского интерфейса часто тестируется вручную, и это дорогостоящий процесс. Процесс, который вполне можно повторять для каждого выпуска.

Andy 13.02.2023 15:16

Спасибо за прояснение, это имеет гораздо больше смысла

AuthorBrotherGames 13.02.2023 16:52
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
66
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы могли бы написать

Enemy.SetBinding(Image.SourceProperty,
    new Binding("Icon")
    {
        Source = enemyViewModel
    });

или

Enemy.SetBinding(Image.SourceProperty,
    new Binding
    {
        Path = new PropertyPath("Icon"),
        Source = enemyViewModel
    });

Однако вам следует рассмотреть возможность использования ItemsControl, который использует Grid в качестве своего ItemsPanel и имеет изображение и привязки в своих ItemTemplate и ItemContainerStyle.

В приведенном ниже примере Enemies будет общедоступным свойством типа ObservableCollection<EnemyViewModel> в вашей модели основного представления.

<ItemsControl ItemsSource = "{Binding Enemies}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    ... 
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    ...
                </Grid.RowDefinitions>
            </Grid>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType = "ContentPresenter">
            <Setter Property = "Grid.Column" Value = "{Binding Column}"/>
            <Setter Property = "Grid.Row" Value = "{Binding Row}"/>
        </Style>
    </ItemsControl.ItemContainerStyle>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Image Source = "{Binding Icon}" Stretch = "Fill"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

См. также Обзор шаблонов данных.

Вы можете определить стиль в словаре ресурсов, чтобы применить эти настройки к изображению.

Вот PoC, который я собрал. Я добавил Skull.png в свой проект (для изображения врага) и установил его в качестве ресурса.

EnemyViewModel имеет жестко закодированные значения, но, конечно, вы должны их установить.

public partial class EnemyViewModel : ObservableObject
{
    [ObservableProperty]
    private int row = 2;

    [ObservableProperty]
    private int column = 3;

    [ObservableProperty]
    private BitmapImage icon = new BitmapImage(new Uri("Skull.png", UriKind.Relative));
 }

Вы бы установили datacontext для своей модели представления врага из своего параметра в своем коде, но здесь я просто создал новый в своем главном окне ctor. Это очень быстро и грязно.

    public MainWindow()
    {
        InitializeComponent();
        var style = Application.Current.Resources["EnemyStyle"] as Style;
        MyGrid.Children.Add(new Image {Style  = style, DataContext=new EnemyViewModel() });
    }

Вместо png я обычно использую пути или кисть для рисования и геометрию. Мой череп довольно простой и может не подходить для контура, так как вы получаете только один цвет для заливки и другой для контура.

Вот как это может выглядеть:

В моем словаре ресурсов у меня есть.

<Geometry x:Key = "Skull">
    M11.2035,16.1L11.103499,18.1C11.5035,18.7 11.9035,19.4 12.0035,20.1 12.203499,19.6 12.403499,19.3 12.703499,18.9 12.803499,18.8 12.803499,18.7 12.603499,18.5 12.403499,18.3 12.203499,18.2 12.0035,17.9 11.803499,17.6 11.803499,17.3 11.7035,17 11.7035,16.7 11.4035,16.4 11.2035,16.1z M10.7035,15.9C10.5035,16.2 10.2035,16.5 10.1035,16.8 9.9035002,17.1 10.0035,17.5 9.8035002,17.8 9.6035002,18.1 9.4035003,18.3 9.2035003,18.5 9.0035004,18.6 9.0035004,18.7 9.1035004,18.9 9.4035003,19.3 9.6035002,19.7 9.8035002,20.1 9.9035002,19.3 10.3035,18.6 10.7035,18z M19.903497,10.3C19.703497,10.3 19.403497,10.4 19.203497,10.5 16.903498,12.1 14.303499,14 11.8035,15.1 15.103498,17.9 21.103497,15.3 20.403497,10.7 20.303497,10.5 20.203497,10.3 19.903497,10.3z M2.0035028,10.3C1.8035026,10.3 1.6035028,10.4 1.6035028,10.7 0.90350294,15.3 6.903501,17.9 10.2035,15.1 7.6035008,14 5.1035016,12.1 2.8035026,10.5 2.4035025,10.4 2.2035027,10.3 2.0035028,10.3z M10.8035,0L11.0035,0C18.203498,0.19999981 25.603496,5 21.403497,14.5 21.403497,14.6 21.303496,14.7 21.203497,14.7 19.503497,17.1 21.703496,19 18.903498,21.2 18.203498,21.2 17.803497,20.9 18.403498,20.1 18.303497,18.5 17.003498,18.9 15.503499,20 15.703498,21.399999 15.303498,22.300001 14.603498,23.5L13.903499,31C13.803499,31.5 13.103499,31.6 12.703499,31.800001 12.703499,28.7 12.903499,26 12.903499,22.899999 14.003499,22.1 13.403499,22.2 12.3035,22.2L12.0035,31.800001C12.103499,31.899999 11.5035,32 10.8035,32 10.7035,29 11.1035,25.6 11.0035,22.6L11.0035,22.5C11.0035,22.2,10.5035,22.2,10.5035,22.399999L10.5035,22.5 10.2035,31.800001C9.9035002,31.899999 9.4035003,31.899999 9.1035004,31.7 9.0035004,28.6 9.1035004,25.399999 9.0035005,22.300001 8.9035005,22.2 8.6035005,22.2 8.5035005,22.300001 8.4035006,25.399999 8.4035006,28.5 8.3035007,31.6 7.8035008,31.399999 7.4035009,31.2 7.2035009,30.7 7.103501,28.2 7.103501,25.6 7.0035011,23.1L7.0035011,22.899999C6.903501,22.5 6.3035014,22.2 6.8035011,20.8 7.103501,20 6.8035011,20.1 6.8035011,19.7 6.4035013,18.7 5.4035015,19.1 3.7035022,20.7L4.6035018,21.2C4.3035021,21.800001 3.7035022,21.399999 3.1035023,20.9 2.9035025,20.7 2.5035024,21.2 2.1035028,21.300001 1.2035027,20.5 0.90350294,19.4 1.4035029,18.6 1.6035028,18.2 1.1035028,17.9 1.2035027,17.5 2.0035028,17.3 2.5035024,17 1.8035026,16.8 0.50350285,13.7 -0.29649639,10.5 0.10350323,6.9999999 0.20350361,2.7 7.103501,0 10.8035,0z
</Geometry>

Стиль

<Style TargetType = "Path" x:Key = "EnemyStyle">
    <Setter Property = "Fill" Value = "Black"/>
    <Setter Property = "Stretch" Value = "Uniform"/>
    <Setter Property = "Data" Value = "{Binding Icon}"/>
    <Setter Property = "Grid.Row" Value = "{Binding Row}"/>
    <Setter Property = "Grid.Column" Value = "{Binding Column}"/>
</Style>

Модель просмотра

public partial class EnemyViewModel : ObservableObject
{
    [ObservableProperty]
    private int row = 2;

    [ObservableProperty]
    private int column = 3;

    [ObservableProperty]
    private Geometry icon = Application.Current.Resources["Skull"] as Geometry;
 }

Другие вопросы по теме