2

Используя шаблон MVVM создаю несколько экземпляров модели данных и упаковываю их в List для хранения в ViewModel. Затем по клику на UserControl привязанном к модели, с помощью RelayCommand, в коллекции будет меняться состояние свойства модели (для простоты цвет фона), а задача UserControl отобразить изменения во View.

BorderControl.xaml

<UserControl x:Class="TestWpfApp.BorderControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:TestWpfApp"
             mc:Ignorable="d">
&lt;UserControl.Resources&gt;
    &lt;Style TargetType=&quot;{x:Type Border}&quot;&gt;
        &lt;Setter Property=&quot;Width&quot; Value=&quot;{Binding Path=Width, RelativeSource={RelativeSource TemplatedParent}}&quot;/&gt;
        &lt;Setter Property=&quot;Height&quot; Value=&quot;{Binding Path=Height, RelativeSource={RelativeSource TemplatedParent}}&quot;/&gt;
        &lt;Setter Property=&quot;Background&quot; Value=&quot;{Binding Path=Background, RelativeSource={RelativeSource TemplatedParent}}&quot;/&gt;
    &lt;/Style&gt;
    &lt;Style TargetType=&quot;{x:Type local:BorderControl}&quot;&gt;
        &lt;Setter Property=&quot;Width&quot; Value=&quot;100&quot;/&gt;
        &lt;Setter Property=&quot;Height&quot; Value=&quot;100&quot;/&gt;            
        &lt;Setter Property=&quot;Margin&quot; Value=&quot;10&quot;/&gt;
        &lt;Setter Property=&quot;Background&quot; Value=&quot;{Binding Path=Background, UpdateSourceTrigger=PropertyChanged}&quot;/&gt;
    &lt;/Style&gt;
&lt;/UserControl.Resources&gt;
&lt;UserControl.Template&gt;
    &lt;ControlTemplate&gt;
        &lt;Border/&gt;
    &lt;/ControlTemplate&gt;
&lt;/UserControl.Template&gt;

</UserControl>

BorderModel.cs

class BorderModel
    {
        public SolidColorBrush Background { get; set; } = new SolidColorBrush(Colors.Wheat);
    }

ViewModel.cs

class ViewModel
    {
        private RelayCommand controlClickedCommand;
    public List&lt;BorderModel&gt; Models { get; set; } = new List&lt;BorderModel&gt;()
    {
        {new BorderModel() }, {new BorderModel()}, {new BorderModel()}
    };

    public RelayCommand ControlClickedCommand =&gt; controlClickedCommand ?? new RelayCommand(ControlClicked);

    private void ControlClicked(object args)
    {
        Models[0].Background = new SolidColorBrush(Colors.Red);
    }
}

MainWindow.xaml

<Window x:Class="TestWpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestWpfApp"        
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>
    <Grid>
        <ItemsControl ItemsSource="{Binding Models}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <local:BorderControl/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</Window>

Тестовый запуск: Тестовый запуск

Для того, чтобы привязаться и выполнить команду ControlClickedCommand в ViewModel я могу использовать InputBindings в BorderControl:

<ControlTemplate>
    <Border>
      <Border.InputBindings>
          <MouseBinding Command="{Binding ControlClickedCommand}" MouseAction="LeftClick"/>
      </Border.InputBindings>
    </Border>
</ControlTemplate>

Но из-за то, что DataContext у Border это BorderModel упакованная в List, не понятно, как привязаться к RelayCommand ControlClickedCommand ViewModel при клике по BorderControl.

aepot
  • 49,560
  • Можно конечно решить, но почему бы команду не перенести в BorderModel? Тогда вопрос решится сам собой. – aepot Apr 01 '21 at 11:28
  • Пока не понимаю как запускать команду из BorderModel, можно подробнее? – Dmitry Chudov Apr 01 '21 at 11:34
  • Берете весь код, связанный с командой, переносите в класс BorderModel, больше ничего не нужно. Ну и вместо Models[0].Background - просто Background – aepot Apr 01 '21 at 11:39
  • у вас кстати ошибка => controlClickedCommand ??, должно быть => controlClickedCommand ??= – aepot Apr 01 '21 at 11:41
  • 1
    А вам не кажется, что у вас тут нарушение MVVM? Вот давайте по порядку: SolidColorBrush Background - цвет, это к чему относится? Это данные, UI или что-то еще? Скорей всего это UI, то есть View слой, верно? Тогда что у вас цвет делает в Model (слой данных)? Ок, предположим это чисто ради примера, но команда, это какой слой? Вот есть 3 слоя: Model - источник данных (база, файл на диске, сервер и др.), View - интерфейс и взаимодействие с пользователем (то, что он видит), и ViewModel - слой, который обрабатывает действия пользователя и делает что-то с Model слоем. Так где должны быть команды? – EvgeniyZ Apr 01 '21 at 11:47
  • Идея в том, чтобы при клике по элементу (слой View) перебирать массив объектов (слой Model) и менять свойства у нужного (слой ViewModel). SolidColorBrush Background приведён для упрощения примера. А команды - это способ взаимодействия между слоями. – Dmitry Chudov Apr 01 '21 at 12:00
  • 1
    А команды - это способ взаимодействия между слоями. вооот, так почему вы ее хотите перенести или как-либо с ней взаимодействовать из Model слоя? Он знать даже не должен что-либо о V и VM слое, а вы его вдруг ответственным за команду сделать хотите. Также мне не понравилось DataContext у Border это BorderModel - то есть вы изначально проигнорировали существование VM слоя и привязались к Model, может из-за этого тогда у вас все трудности? – EvgeniyZ Apr 01 '21 at 12:14
  • Смотрите как обычно это делается: при клике по элементу - в VM слое команда и ее обработчик, который по клику вызывает метод из модели. Сама команда естественно привязана в V слое. Также рядом с командой находится список VM объектов, которые являются оберткой над M объектами, они в себе содержат лишь то, что необходимо для View слоя, для привязок. Далее, перебирать массив объектов - это обычно делают методом в M слое, который возвращает после завершения нужный список обратно. и менять свойства у нужного - подписываетесь на событие обновления коллекции и все изменения переносите в M слой – EvgeniyZ Apr 01 '21 at 12:19
  • К сожалению, так и не получил ответа на свой вопрос. Зато критики, хотя и не просил, получил достаточно. Например: Также мне не понравилось DataContext у Border это BorderModel - странно, что у View контекст данных это Model в MVVM, да? Вопрос был в том, как из контекста данных UserControl получить доступ к контексту данных VM. – Dmitry Chudov Apr 05 '21 at 06:58
  • Очень помогла вот эта ссылка, всё подробно расписано, @EvgeniyZ, спасибо! – Dmitry Chudov Apr 05 '21 at 06:59

0 Answers0