powered by simpleCommunicator - 2.0.34     © 2025 Programmizd 02
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Правильное размещение функционала и привязки команд
25 сообщений из 37, страница 1 из 2
Правильное размещение функционала и привязки команд
    #39725813
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Наверное, замучил всех своими "детскими" вопросами... Извините если, что .... Но я опять за своё :)
У меня вопрос по правильному размещению функционала и привязки команд.
Для объяснения простой пример.
Код ViewModel - коллекция строк и методы для добавления, удаления и изменения элементов коллекции, и свойства для биндинга команд
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
namespace WpfQuestions
{
    public class ExampleCommandVM : INotifyPropertyChanged
    {
        private ObservableCollection<string> _listSource = new ObservableCollection<string>();
        private int maxId = 0;
        #region Событие PropertyChanged и метод для его вызова
        /// <summary>Событие для извещения об изменения свойства</summary>
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>Метод для вызова события извещения об изменении свойства</summary>
        /// <param name="prop">Изменившееся свойство</param>
        public void OnPropertyChanged([CallerMemberName]string prop = "")
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
        #endregion

        public ObservableCollection<string> ListSource { get => _listSource; set { _listSource = value; OnPropertyChanged(); } }

        public ExampleCommandVM()
        {
        }
        static public ExampleCommandVM Create()
        {
            ExampleCommandVM ret = new ExampleCommandVM();
            for (int ind = 0; ind < 5; ind++)
                ret.AddNew();

            return ret;
        }

        public void AddNew()
        {
            ListSource.Add("Строка №" + maxId.ToString().PadLeft(2, '0'));
            maxId++;
        }

        public void Remove(string Value)
        {
            ListSource.Remove(Value);
        }

        public void Save(string oldValue, string newValue)
        {
            ListSource[ListSource.IndexOf(oldValue)] = newValue;
        }

        private ICommand _commAdd;
        public ICommand CommAdd => _commAdd ?? (_commAdd = new RelayCommand(o => { AddNew(); }));

        private ICommand _commRemove;

        public ICommand CommRemove => _commRemove ?? (_commRemove = new RelayCommand(o => { Remove((string)o); }));
    }

    public class RelayCommand : ICommand
    {
        private Action<object> execute;
        private Func<object, bool> canExecute;

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
        {
            this.execute = execute;
            this.canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            return this.canExecute == null || this.canExecute(parameter);
        }

        public void Execute(object parameter)
        {
            this.execute(parameter);
        }
    }
}

Xaml окна
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
<Window x:Name="window" x:Class="WpfQuestions.ExampleCommand"
        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:WpfQuestions"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d" SizeToContent="WidthAndHeight"
        Title="ExampleCommand" >
    <d:Window.DataContext>
         <local:ExampleCommandVM>
            <local:ExampleCommandVM.ListSource>
                <sys:String>Строка 1</sys:String>
                <sys:String>Строка 2</sys:String>
                <sys:String>Строка 3</sys:String>
                <sys:String>Строка 4</sys:String>
            </local:ExampleCommandVM.ListSource>
        </local:ExampleCommandVM>
    </d:Window.DataContext>
    <Window.Resources>
        <Style TargetType="ListBoxItem" x:Key="ListBoxItemExamp">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition/>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <TextBox Text="{Binding Mode=OneWay}" VerticalAlignment="Center"/>
                            <Button Grid.Column="1" Margin="5" Padding="2">Save</Button>
                            <Button Grid.Column="2" Margin="5" Padding="2" Command="{Binding DataContext.CommRemove, ElementName=window}" CommandParameter="{Binding}" >Remove</Button>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <StackPanel>
        <ListBox ItemsSource="{Binding ListSource}" ItemContainerStyle="{StaticResource ListBoxItemExamp}"/>
        <Button Margin="10" Command="{Binding CommAdd}">Add</Button>
    </StackPanel>
</Window>

Привязка к VM в коде окна
Код: c#
1.
2.
3.
4.
5.
6.
7.
        private ExampleCommandVM vm;
        public ExampleCommand()
        {
            InitializeComponent();
            vm = ExampleCommandVM.Create();
            DataContext = vm;
        }


С кнопкой Add - вопросов ни каких. Она биндится в VM из View. Вроде все принципы WPF и MVVM соблюдаются.
С кнопкой Remove - уже у меня сомнения. Приходится из шаблона биндить её к свойству DataContext окна. Но, по идее, шаблон не должен ничего знать про окно. Понимаю, что не правильно, но не знаю как сделать правильно.
С кнопкой Save - вообще, ступор. В метод сохранения надо передать текущее значение строки (оно служит идентификатором, который я для уменьшения кода примера не вводил) и новое значение из TextBox (он связан с VM в режиме OneWay). Как их оба одновременно передать не соображу ни как.
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39725844
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
команды всплывают по визуальному дереву, так как работают на маршрутизированных команда, их можно обрабатывать на уровне ListBox, а не ListBoxItem
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39725914
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Roman Mejtesкоманды всплывают по визуальному дереву, так как работают на маршрутизированных команда, их можно обрабатывать на уровне ListBox, а не ListBoxItemНо саму же команду надо как-то присоединить к кнопке. Как это можно сделать не ссылаясь на общую VM, а используя информацию доступную только ListBoxItem (ControlTemplate )?
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39725920
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Eld HaspНо саму же команду надо как-то присоединить к кнопке.Команду можно присоединить к кнопке на уровне ListBoxItem. И при нажатии кнопки команда начнет всплывать. А ловить и переадресовывать ее на RelayCommand нужно уже выше .
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39725936
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Shocker.ProEld HaspНо саму же команду надо как-то присоединить к кнопке.Команду можно присоединить к кнопке на уровне ListBoxItem. И при нажатии кнопки команда начнет всплывать. А ловить и переадресовывать ее на RelayCommand нужно уже выше .Не дошло... Если команда у меня на уровне VM, как её присоединить на уровне ListBoxItem? На уровне ListBoxItem этой же команды нет. Или надо какую-то другую команду присоединить?
Можно здесь подробнее и на примере.
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39725944
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Мне тут как-то в голову пришла гениальная идея для таких случаев, хотя может она и банальная, интересно мнение Романа на этот счет. Берем простейший класс без визуального оформления:
Код: c#
1.
2.
3.
	public class ContextHolder : ContentControl
	{
	}



Используем его как контейнер для ListBox, при этом явно привязавшись к модели
Код: xml
1.
2.
3.
4.
    <stw:ContextHolder DataContext="{Binding}">
      <ListBox ..........>
      </ListBox>
    </stw:ContextHolder>



А затем внутри шаблона для ListBoxItem-а просто ссылаемся на него
Код: xml
1.
<AnyTag AnyProperty="{Binding DataContext.MyProperty, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=stw:ContextHolder}" />

Я не пробовал это с командами - но что мешает свойство Command привязать таким образом к VM (не забыв про CommandParameter="{Binding}", чтобы знать, какой именно элемент вызывал команду)
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39725951
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Eld HaspЕсли команда у меня на уровне VM, как её присоединить на уровне ListBoxItem? На уровне ListBoxItem этой же команды нетТы путаешь саму команду, которая определяется как public static readonly и видна отовсюду и привязку ее к ICommand. Нужно просто еще раз повнимательней перечитать теорию.

Однако, выше я привел пример, который, вероятно, позволит обойтись без всплывающей команды, привязавшись напрямую из кнопки к модели.
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726020
Сон Веры Павловны
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Для биндинга элемента ItemsControl на свойство датаконтекста самого ItemsControl я обычно использую решение Джоша Смита. А если в параметр команды нужно передать составное значение, состоящее частично из значений свойст датаконтекста вложенного элемента, частично - из значений свойств датаконтекста вышестоящего элемента, то вышепредложенный ElementSpy+мультибиндинг+IMultuValueConverter эту проблему вполне решают.
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726116
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В данном примере, при вызове команды ApplicationCommands.Delete для элемента списка, будет вызвана команда DeleteCommand из модели MainModel и элемент удалится. Кнопка удаления как и в вашем примере находится в шаблоне элемента списка.
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
<Window x:Class="CommandExample.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:CommandExample"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainModel/>
    </Window.DataContext>
    <Grid>
        <local:ContextHolder>
            <local:ContextHolder.Commands>
                <local:CommandBinding RoutedCommand="{x:Static ApplicationCommands.Delete}" 
                                      RelayCommand="{Binding DeleteCommand}"/>
            </local:ContextHolder.Commands>
            <ListBox ItemsSource="{Binding Persones}">
                <ListBox.ItemTemplate>
                    <DataTemplate DataType="{x:Type local:Person}" >
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <TextBlock Grid.Column="0" Text="{Binding Name}" Margin="5"/>
                            <Button Grid.Column="1" Content="Remove" Padding="5,2" Margin="5"
                                    Command="{x:Static ApplicationCommands.Delete}" 
                                    CommandParameter="{Binding}"/>
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </local:ContextHolder>
    </Grid>
</Window>


Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace CommandExample
{
    public class CommandsCollection : FreezableCollection<CommandBinding> { }

    public class CommandBinding : Freezable
    {
        public ICommand RoutedCommand
        {
            get { return (ICommand)GetValue(RoutedCommandProperty); }
            set { SetValue(RoutedCommandProperty, value); }
        }
        public static readonly DependencyProperty RoutedCommandProperty =
            DependencyProperty.Register("RoutedCommand", typeof(ICommand), typeof(CommandBinding), new PropertyMetadata(null));


        public ICommand RelayCommand
        {
            get { return (ICommand)GetValue(RelayCommandProperty); }
            set { SetValue(RelayCommandProperty, value); }
        }
        public static readonly DependencyProperty RelayCommandProperty =
            DependencyProperty.Register("RelayCommand", typeof(ICommand), typeof(CommandBinding), new PropertyMetadata(null));

        protected override Freezable CreateInstanceCore()
        {
            return new CommandBinding();
        }
    }

    public class ContextHolder : ContentControl
    {
        private readonly Dictionary<ICommand, CommandBinding> _dict;

        public CommandsCollection Commands
        {
            get { return (CommandsCollection)GetValue(CommandsProperty); }
            set { SetValue(CommandsProperty, value); }
        }

        public static readonly DependencyProperty CommandsProperty =
            DependencyProperty.Register("Commands", typeof(CommandsCollection), typeof(ContextHolder), new PropertyMetadata(null));

        public ContextHolder()
        {
            Commands = new CommandsCollection();
            Commands.Changed += FreezableCollectionChanged;
            _dict = new Dictionary<ICommand, CommandBinding>();

            CommandManager.AddCanExecuteHandler(this, CanExecute);
            CommandManager.AddExecutedHandler(this, OnExecute);
        }

        private void FreezableCollectionChanged(object sender, EventArgs e)
        {
            _dict.Clear();
            foreach (var i in Commands) { _dict.Add(i.RoutedCommand, i); }
        }


        private void OnExecute(object sender, ExecutedRoutedEventArgs e)
        {
            if (_dict.TryGetValue(e.Command, out CommandBinding binding))
            {
                if (binding?.RelayCommand != null) binding.RelayCommand.Execute(e.Parameter);
            }
        }

        private void CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (_dict.TryGetValue(e.Command, out CommandBinding binding))
            {
                if (binding?.RelayCommand != null) e.CanExecute = binding.RelayCommand.CanExecute(e.Parameter);
            }
        }
    }
}


Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace CommandExample
{
    public class Person
    {
        public Person(string name, int age)
        {
            Name = name;
            Age = age;
        }

        public string Name { get; }
        public int Age { get; }
    }

    public class MainModel
    {
        private ICommand _deleteCommand;
        public ICommand DeleteCommand => _deleteCommand ?? (_deleteCommand = new RelayCommand(OnDelete));

        private void OnDelete(object obj)
        {
            if (obj is Person person) Persones.Remove(person);
        }

        public ObservableCollection<Person> Persones { get; } = new ObservableCollection<Person>()
        {
            new Person("Roman", 36),
            new Person("Sofia", 46)
        };
    }

    public class RelayCommand : ICommand
    {

        public event EventHandler CanExecuteChanged;
        private readonly Func<object, bool> _canExecute;
        private readonly Action<object> _onExecute;

        public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
        {
            _onExecute = execute;
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            if (_canExecute == null) return true;
            return _canExecute.Invoke(parameter);
        }

        public void Execute(object parameter)
        {
            _onExecute?.Invoke(parameter);
        }
    }
}


можно, примерно, вот так. Только тут всё на коленке сделано, лишь бы работало. Но пример рабочий и используется вместо своей команды, ApplicationCommand'а, а лучше своей пользоваться
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726488
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Roman MejtesВ данном примере, при вызове команды ApplicationCommands.Delete для элемента списка, будет вызвана команда DeleteCommand из модели MainModel и элемент удалится. Кнопка удаления как и в вашем примере находится в шаблоне элемента списка
......
можно, примерно, вот так. Только тут всё на коленке сделано, лишь бы работало. Но пример рабочий и используется вместо своей команды, ApplicationCommand'а, а лучше своей пользоватьсяДа, мне бы лишь принцип понять.
А в целом, правильно ли биндить, элементы шаблона (или DataTemplate) не к свойствам Item? Может более верным будет создание в объекте Item'а нужных свойств во ViewModel и биндить к ним? В принципе (почитал ещё вчера, сегодня), в этом отношении команда - это тоже свойство, метод и его можно создать в каждом объекте Item'а.
Как концептуально, белее верно это будет сделать?
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726497
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Eld HaspКак концептуально, белее верно это будет сделать?ты сделал оба варианта? Тогда уже сам можешь сделать вывод что тебе проще.
Паттерн MV**** никто 100 процентно по буквам не разделяет. Т..к.с некоторого момента издержки больше профита.
Главное что вьюКонтроллер класс без гуи полностью поддерживает состояние объекта.
Imho
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726505
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Eld HaspА в целом, правильно ли биндить, элементы шаблона (или DataTemplate) не к свойствам Item? Может более верным будет создание в объекте Item'а нужных свойств во ViewModel и биндить к ним? В принципе (почитал ещё вчера, сегодня), в этом отношении команда - это тоже свойство, метод и его можно создать в каждом объекте Item'а.
Как концептуально, белее верно это будет сделать?
Можно, но ведь под ListBoxItem у тебя отдельная вьюмодель и она по-правильному ничего не знает о своем родителе. Но ведь информация о нажатой кнопке нужна не самому элементу, а родителю. Тогда в родительской модели придется подписываться на события модели Item-а. А это значит, что придется обрабатывать добавление и удаление Item-ов в коллекцию (отписываться тоже обязательно, чтобы не было утечки памяти), то есть либо организовывать прокси для записи в коллекцию, либо отслеживать все события изменения коллекции.

В общем, гораздо проще воспользоваться всплытием команды.
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726514
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Eld HaspКак концептуально, белее верно это будет сделать?Вот допустим у меня есть ListBox отображающий два свойства Name, Vaulue.
Значение Value индивидуально для каждого Item'a, а Name оно одно и тоже. И меняется в зависимости от какого-то внешнего (по отношению к Item) свойства ViewModel. Я сейчас делаю таким образом. Когда создаю список для ListBox, то в ViewModel прописываю в каждом свойстве Name списка одно и тоже значение из общего свойства.
Верный ли такой подход? Если верный, то может верным будет и поступить так с командами?
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726526
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Eld HaspЕсли верный, то может верным будет и поступить так с командами?нет. Name - это свойство уровня модели. Команда - это уровень представления. Команда - свойство кнопки на форме, на уровне модели кнопки нет.

Можно на уровне модели обработать команду кнопки. И если нажатие кнопки нужно обрабатывать только в пределах модели Item-а, то это годится. Но если нажатие кнопки в Item-е требуется отслеживать выше по иерархии - этот подход превратится в головняк (см, что я писал выше)
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726528
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Shocker.ProМожно, но ведь под ListBoxItem у тебя отдельная вьюмодель и она по-правильному ничего не знает о своем родителе. Но ведь информация о нажатой кнопке нужна не самому элементу, а родителю. Тогда в родительской модели придется подписываться на события модели Item-а. А это значит, что придется обрабатывать добавление и удаление Item-ов в коллекцию (отписываться тоже обязательно, чтобы не было утечки памяти), то есть либо организовывать прокси для записи в коллекцию, либо отслеживать все события изменения коллекции.

В общем, гораздо проще воспользоваться всплытием команды.Нет, может я Вас не правильно понимаю, может я не правильно свою мысль изложил.
Сделать на уровне VM в каждом объекте Item'а свойство ссылающееся на общую команду. То есть, то что делалось в шаблоне с привязкой команды к DataContext окна (это же VM), сделать явно в VM. И тогда в шаблоне будет привязка команды к свойству Item'а.

Получается VM знает о связях между отображаемыми данными - но это ей и положено знать.
А View этой информацией не владеет, и может работать замкнуто внутри себя.
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726533
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Shocker.Proнет. Name - это свойство уровня модели. Команда - это уровень представления. Команда - свойство кнопки на форме, на уровне модели кнопки нет.

Можно на уровне модели обработать команду кнопки. И если нажатие кнопки нужно обрабатывать только в пределах модели Item-а, то это годится. Но если нажатие кнопки в Item-е требуется отслеживать выше по иерархии - этот подход превратится в головняк (см, что я писал выше)Команда - уровень представления. Но привязки для неё создаются в VM. Методы которые она вызывает - это, по моему, вообще, уровень модели. Просто в примере модели для простоты нет, но теоретически изменять данные должна именно модель. Разве не так?

То что Вы пишите - я внимательно читаю. И пытаюсь "переварить". Отсюда и "детские" вопросы.
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726541
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Eld HaspСделать на уровне VM в каждом объекте Item'а свойство ссылающееся на общую команду. То есть, то что делалось в шаблоне с привязкой команды к DataContext окна (это же VM), сделать явно в VM. И тогда в шаблоне будет привязка команды к свойству Item'а.Ссылка из ребенка на родителя - фиговая идея. Не делайте так в принципе (не только тут).
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726588
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.Pro,

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

Eld Hasp,
Команды тут, слишком хорошо заходят, чтоб их игнорировать, это универсальный инструмент, который используется в WPF приложениях. Это их предназначение и не надо придумывать велосипед.

RoutedCommand сама по себе определена на уровне View и является часть представления. Никакой связи с моделью представления такая команда не имеет и действует только в пределах визального дерева.
Какой то элемент управления "возбуждает" маршрутизируемую команду, а какой то элемент выше по дереву её обрабатывает.
Если вы посмотрите реализацию элементов управления в WPF, то там так и сделано (команды ApplicationCommands и д.р.). И для такого функционала никакая модель представления не нужна, по сути команда это обычное RoutedEvent.
Но так как часто необходимо команду выполнить на уровне VM команду можно вызвать напрямую из модели представления через связывание (Binding), либо как я привел пример выше, скомбинировать оба подхода, на одному уровне представление возбуждается команда, а любом уровне выше команда обрабатывается.

Не надо забывать, что каждая ссылка на другой объект, это L байт и если список элементов очень большой, умножайте на N.
GC приходится работать дольше. Всё это ударит по перфомансу и если сейчас это не критично, то потом может сыграть злую шутку.
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726603
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Roman Mejtesесли у всех элементов дерева одинаковое время жизни, то это не так критично. Циклические ссылки сборщиком учитываются, все будет очищено, как только будет удалена ссылка на корневой элемент.Да, но десктопное приложение может не закрываться днями и неделями. При этом, если список элементов подвержен фильтрации, то он может обновляться часто, полностью очищаясь. И если забыть про отписку от событий, все элементы очищаемого списка из памяти не исчезнут - здравствуй утечка. Можно, конечно, использовать слабосвязанные события, но тут нет никакого смысла.
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726607
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.Proесли список элементов подвержен фильтрацииимеется ввиду, конечно, не встроенная фильтрация представления, а именно перезагрузка списка
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726678
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Roman MejtesВ данном примере, при вызове команды ApplicationCommands.Delete для элемента списка.........Смотрю я на Ваши примеры и охватывает меня уныние от понимания ничтожности собственных знаний.......... :(
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726685
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
[quot Shocker.Pro]Roman Mejtes......... И если забыть про отписку от событий, все элементы очищаемого списка из памяти не исчезнут - здравствуй утечка .............. То есть привязка команды содержит в себе подписку на событие? И при изменении списка, если в нём содержатся привязанные команды, элементы списка не отправятся в мусор? Из-за подписки на событие они будут оставаться в памяти?
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39726692
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Eld HaspТо есть привязка команды содержит в себе подписку на событие? И при изменении списка, если в нём содержатся привязанные команды, элементы списка не отправятся в мусор? Из-за подписки на событие они будут оставаться в памяти?Нет. Я же говорил про обычные события внутри вьюмодели.
Команды как раз-таки построены на слабосвязанных событиях, так что за них можно не раздумывать.
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39727595
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Что-то долго до меня всё доходит.
С вашей помощью и подсказками, сделал так.
Код ViewModel
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
    public class ExampleCommandVM 
    {
        private int maxId = 0;

        public ObservableCollection<string> ListSource { get; set; } = new ObservableCollection<string>();

        public ExampleCommandVM()
        {
        }
        static public ExampleCommandVM Create()
        {
            ExampleCommandVM ret = new ExampleCommandVM();
            for (int ind = 0; ind < 5; ind++)
                ret.OnAdd();
            return ret;
        }

        public void OnAdd(object Value = null)
        {
            if (Value is string value)
                ListSource.Add(value);
            else
                ListSource.Add("Строка №" + maxId.ToString().PadLeft(2, '0'));
            maxId++;
        }

        public void OnRemove(object Value)
        {
            if (Value is string str) ListSource.Remove(str);
        }

        public void OnSave(object changeValue)
        {
            if (!(changeValue is ChangeValue chgVal)) return;
            ListSource[ListSource.IndexOf(chgVal.OldValue)] = chgVal.NewValue;
        }

        private ICommand _addComm;
        public ICommand AddComm => _addComm ?? (_addComm = new RelayCommand(OnAdd));

        private ICommand _removeComm;
        public ICommand RemoveComm => _removeComm ?? (_removeComm = new RelayCommand(OnRemove));

        private ICommand _saveComm;
        public ICommand SaveComm => _saveComm ?? (_saveComm = new RelayCommand(OnSave));

    }


CommandsCollection и RelayCommand - не менял. Полностью из поста 21720777 от Roman Mejtes .

Словарь ExampleCommandDict.xaml с шаблонам и конверторами
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:WpfQuestions">
    <local:ChangeValueConverter x:Key="ChangeValueConverter"/>
    <local:IsChangeValueConverter x:Key="IsChangeValueConverter"/>
    <Style TargetType="ListBoxItem" x:Key="ListBoxItemExamp">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ListBoxItem">
                    <Grid x:Name="PART_Grid" Background="{TemplateBinding Background}" >
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Text="{Binding Mode=OneWay}" VerticalAlignment="Center" Margin="5"/>
                        <TextBox x:Name="PART_TextBox" Grid.Column="1" Text="{Binding Mode=OneWay}" VerticalAlignment="Center" Margin="5"/>
                        <Button x:Name="PART_Button_Save" Content="Save" Grid.Column="2" Margin="5" Padding="2"
                                    Command="{x:Static ApplicationCommands.Save}" 
                                    >
                            <Button.CommandParameter>
                                <MultiBinding Converter="{StaticResource ChangeValueConverter}">
                                    <Binding/>
                                    <Binding ElementName="PART_TextBox" Path="Text"/>
                                </MultiBinding>
                            </Button.CommandParameter>
                        </Button>
                        <Button Content="Remove" Grid.Column="3" Margin="5" Padding="2" 
                                    Command="{x:Static ApplicationCommands.Delete}" 
                                    CommandParameter="{Binding}" />
                    </Grid>
                    <ControlTemplate.Triggers>
                        <DataTrigger Value="True">
                            <DataTrigger.Binding>
                                <MultiBinding Converter="{StaticResource IsChangeValueConverter}">
                                    <Binding/>
                                    <Binding  ElementName="PART_TextBox" Path="Text"/>
                                </MultiBinding>
                            </DataTrigger.Binding>
                            <Setter TargetName="PART_TextBox" Property="Foreground" Value="Red"/>
                            <Setter TargetName="PART_Button_Save" Property="IsEnabled" Value="True"/>
                        </DataTrigger>
                        <DataTrigger Value="False">
                            <DataTrigger.Binding>
                                <MultiBinding Converter="{StaticResource IsChangeValueConverter}">
                                    <Binding/>
                                    <Binding  ElementName="PART_TextBox" Path="Text"/>
                                </MultiBinding>
                            </DataTrigger.Binding>
                            <Setter TargetName="PART_TextBox" Property="Foreground" Value="Black"/>
                            <Setter TargetName="PART_Button_Save" Property="IsEnabled" Value="False"/>
                        </DataTrigger>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Background" Value="LightBlue"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        
    </Style>
</ResourceDictionary>

И само окно ExampleCommand.xaml
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
<Window x:Name="window" x:Class="WpfQuestions.ExampleCommand"
        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:WpfQuestions"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d" SizeToContent="WidthAndHeight"
        Title="ExampleCommand" >
    <d:Window.DataContext>
        <local:ExampleCommandVM>
            <local:ExampleCommandVM.ListSource>
                <sys:String>Строка 1</sys:String>
                <sys:String>Строка 2</sys:String>
            </local:ExampleCommandVM.ListSource>
        </local:ExampleCommandVM>
    </d:Window.DataContext>
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="ExampleCommandDict.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <local:ContextHolder>
        <local:ContextHolder.Commands>
            <local:CommandBinding RoutedCommand="{x:Static ApplicationCommands.Delete}"
                                      RelayCommand="{Binding RemoveComm}"
                                      />
            <local:CommandBinding RoutedCommand="{x:Static ApplicationCommands.Save}"
                                      RelayCommand="{Binding SaveComm}"
                                      />
            <local:CommandBinding RoutedCommand="{x:Static ApplicationCommands.New}"
                                      RelayCommand="{Binding AddComm}"
                                      />
        </local:ContextHolder.Commands>
        <StackPanel>
            <ListBox ItemsSource="{Binding ListSource}" ItemContainerStyle="{StaticResource ListBoxItemExamp}"/>
            <Button Margin="10" Content="Add" Command="{x:Static ApplicationCommands.New}"/>
        </StackPanel>
    </local:ContextHolder>


</Window>



Из того, что понял.
- Каких-то "стандартных" методов для подключения кастомных команд в списочных элементах в WPF не предусмотрено. Поэтому и пришлось создавать класс ContextHolder, который отлавливает команды из вложенных элементов и в котором можно указать привязки команд на методы VM.
- Для "отлова команды" в классе CommandBinding используется привязка к одной из команд класса RoutedUICommand. Это обязательное условие?
- Если использовать напрямую команды из класса RoutedUICommand, то они требуют присоединения обработчика в самом окне на уровне View. Какая-то, как мне кажется, не завершённость в реализации... Или я опять чего-то не до понял?
...
Рейтинг: 0 / 0
Правильное размещение функционала и привязки команд
    #39727602
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Eld Hasp- Для "отлова команды" в классе CommandBinding используется привязка к одной из команд класса RoutedUICommand. Это обязательное условие?Это не привязка. Я советовал перечитать, что собой представляет команда. Команда - это просто экземпляр класса "RoutedUICommand", присвоенный статической переменной. Команда САМА ПО СЕБЕ ничего не делает - это просто маркер.

Button заставляет всплывать экземпляр, присвоенный статической переменной ApplicationCommands.New. Он отлавливается уровнем выше с помощью local:CommandBinding. Но этот "уловитель" должен же знать, на какую команду реагировать, иначе он будет реагировать на все команды, которые придут "снизу". Поэтому в его свойстве RoutedCommand указывается тот самый маркер, который нужно отлавливать именно ему.
...
Рейтинг: 0 / 0
25 сообщений из 37, страница 1 из 2
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Правильное размещение функционала и привязки команд
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]