Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.73/11: Рейтинг темы: голосов - 11, средняя оценка - 4.73
1 / 1 / 0
Регистрация: 20.08.2021
Сообщений: 15

WPF / UWP DataGrid какое событие возникает после изменения кол-ва строк?

12.12.2021, 14:38. Показов 2145. Ответов 14
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Привет всем!

Собственно сабж)

Вроде всё перепробовал... (манипуляция, лоадед...)
(нужно именно после!)

Заранее спасибо ВС
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
12.12.2021, 14:38
Ответы с готовыми решениями:

Какое событие должно быть после изменения в Alert
Всем привет, такой вопрос, какое событие служит для перерисовки интерфейса (т.к. в базе обновились данные) после закрытия Alert окна, а...

Какое событие нужно использовать для изменения цвета и т д строк DataGridView при его отрисовке
Подскажите, какое событие нужно использовать для изменения цвета и т д строк DataGridView при его отрисовке, на основе значений,...

Какое событие возникает при...
Какое событие возникает при нажатие мышкой на ScrollBarе StringGrida. когда нажимаем на кнопки со стрелками или двигаем ползунок?

14
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16124 / 11248 / 2888
Регистрация: 21.04.2018
Сообщений: 33,082
Записей в блоге: 2
12.12.2021, 16:49
Цитата Сообщение от VovikDoc99 Посмотреть сообщение
DataGrid какое событие возникает после изменения кол-ва строк?
Никакое.

DataGrid - это производный от ItemsControl и строки генерируются в нём.
А ItemsControl, вообще, не никаких событий.
Если шибко приспичило можно подписаться на изменение Items.CollectionChanged или на Items.PropertyChanged и следить за Items.Count.

Но в целом такая задача, мягко говоря, не типична для UWP/WPF.
Это инструменты создания GUI (представления Данных), а не средства работы с Данными.
Если изменилось количество строк, значит изменилась коллекция которую представляет ItemsControl.
Вот за коллекцией и надо следить.
Почему она у вас изменяется?
0
1 / 1 / 0
Регистрация: 20.08.2021
Сообщений: 15
12.12.2021, 18:47  [ТС]
Вообще-то это нужно, чтобы связать SelectedItems - асинхронной командой переносятся из одного грида коллекция в другой и нужно, чтобы эта коллекция была Selected, чтобы просто было отменить/видеть, что выбрано/остался в памяти этот набор и пр.
Grid1 - все предметы, за исключение выбранных
Grid2 - все, выбранные предметы

как-то так
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16124 / 11248 / 2888
Регистрация: 21.04.2018
Сообщений: 33,082
Записей в блоге: 2
12.12.2021, 20:23
Цитата Сообщение от VovikDoc99 Посмотреть сообщение
Вообще-то это нужно....
В SelectedItems у вас находится IList.
Вам нужно пройти циклом по этой коллекции и каждый элемент преобразовать к исходному типу и потом обработать.
Совершенно не понятно причём здесь события.
Вы же сами пишите асинхронной командой.
Команда же вызывается кнопкой, и не зависит от событий DataGrid.
В параметр команды передайте DataGrid.SelectedItems и больше ничего не надо.
0
1 / 1 / 0
Регистрация: 20.08.2021
Сообщений: 15
12.12.2021, 22:45  [ТС]
вроде справился, но как мне кажется через ужасный костыль.
Делаю в VM интерфейс:
C#
1
2
3
4
5
public interface IAdvancedSelectedItems 
{
    void SelectItems(IEnumerable<ProxyEntity> items);
    void SelectItem(ProxyEntity? item = null);
}
и когда нужно убновляю.
Далее гриды модифицирую этим интерфейсом:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class AdvancedDataGrid : DataGrid, IAdvancedSelectedItems
{
    public void SelectItems(IEnumerable<ProxyEntity> items)
    {
        SelectedIndex = -1;
        foreach (var item in items)
        {
            SelectItem(item);
        }
    }
 
    public void SelectItem(ProxyEntity? item = null)
    {
        if (item is null)
        {
            SelectedIndex = -1;
            return;
        }
        if (Items.Contains(item)) SelectedItems.Add(item);
    }
}
затем не забываем о ручном выборе:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void InOutGrid_MouseUp(object sender, MouseButtonEventArgs e)
{
    var grid = sender as AdvancedDataGrid ?? throw new Exception();
    foreach (var item in grid.Items)
    {
        if (item is not ProxyEntity proxy) return;
        if (grid.SelectedItems.Contains(proxy))
        {
            if (!_model.SelectedProxies.Contains(proxy))
                _model.SelectedProxies.Add(proxy);
        }
        else
        {
            if (_model.SelectedProxies.Contains(proxy))
                _model.SelectedProxies.Remove(proxy);
        }
    }
}
не нравится всё мне это(((
но по другому, вроде никак...

Добавлено через 10 минут
Команда же вызывается кнопкой, и не зависит от событий DataGrid.
В параметр команды передайте DataGrid.SelectedItems и больше ничего не надо.
ДА именно всё так! вся проблема в том, что SelectedItems не имеют сеттера!
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16124 / 11248 / 2888
Регистрация: 21.04.2018
Сообщений: 33,082
Записей в блоге: 2
12.12.2021, 23:00
Цитата Сообщение от VovikDoc99 Посмотреть сообщение
ДА именно всё так! вся проблема в том, что SelectedItems не имеют сеттера!
А зачем он нужен?
Ведь не SelectedItems надо привязывать.
К параметр команды привязывать к нему.
Чё-то я вас не до понимаю.
Покажите XAML с этим DataGrid и кнопкой в которой команда для обработки.
0
1 / 1 / 0
Регистрация: 20.08.2021
Сообщений: 15
12.12.2021, 23:57  [ТС]
Никаких наворотов: [XML]<controls:AdvancedDataGrid Grid.Row="2"
x:Name="OutGrid"
ItemsSource="{Binding OutProxies, UpdateSourceTrigger=PropertyChanged}"
SelectionChanged="DataGrid_SelectionChan ged"
MouseUp="InOutGrid_MouseUp">
<DataGrid.Columns>
<DataGridTextColumn Header="Включить в список:"
Binding="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
Width="*"/>
</DataGrid.Columns>
</controls:AdvancedDataGrid>
<Button Grid.Row="3"
Command="{Binding AsyncSetCommand}"
CommandParameter="{Binding ElementName=OutGrid, Path=SelectedItems}"
Content="
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16124 / 11248 / 2888
Регистрация: 21.04.2018
Сообщений: 33,082
Записей в блоге: 2
13.12.2021, 02:04
VovikDoc99, по интерфейсу Форума:
 Комментарий модератора 
При обращении к другому пользователю указывайте его ник в тегах [NICK][/NICK] или цитируйте часть сообщения на которое отвечаете.
В противном случае ему не придёт уведомление о вашем обращении и вы можете не дождаться ответа на своё сообщение.

Для вставки ника: введите ник, выделите его и нажмите кнопку "Динамик" на панели редактора сообщений.
Или кликните по нику автора сообщения в панели слева от текста его сообщения.

Для вставки цитаты: выделите нужную цитату, должна появиться всплывающая кнопка "Цитировать", нажмите её.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16124 / 11248 / 2888
Регистрация: 21.04.2018
Сообщений: 33,082
Записей в блоге: 2
13.12.2021, 02:14
Цитата Сообщение от VovikDoc99 Посмотреть сообщение
Никаких наворотов:
Всё правильно.
У вас есть привязанный CommandParameter.
А для чего, вы писали выше, вам понадобился сеттер у SelectedItems?

P.S. Не относится к вопросу, но всё же.
По принятым правилам именования, у асинхронных методов есть постфикс Async.
Поэтому вам лучше переименовать команду в SetCommandAsync.

Второй аспект, очень не рекомендую использовать асинхронные команды.
Они имеют смысл только в развитой реализации где инкапсулируется ей занятость и валидация.
Для простого асинхронного вызова метода лучше использовать обычную команду.
Это даёт гораздо большую прозрачность и контролируемость коду.

Пример реализации и использования асинхронной команды.
Вы явно такое не делаете и, следовательно, она вам не нужна, а только внесёт путаницу в ваш код.
An example of my implementation of base classes: BaseInpc, RelayCommand, RelayCommandAsync, RelayCommand<T>, RelayCommandAsync<T>.
0
 Аватар для Andrey-MSK
3336 / 2223 / 387
Регистрация: 14.08.2018
Сообщений: 7,490
Записей в блоге: 4
13.12.2021, 09:11
Лучший ответ Сообщение было отмечено VovikDoc99 как решение

Решение

VovikDoc99,
Ставите из NuGet в слой с View пакет - Microsoft.Xaml.Behaviors.Wpf
Пишете в слое с View вот такой Behavior - создает свойство с SelectedItems
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
using System.Collections;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
 
using Microsoft.Xaml.Behaviors;
 
namespace DBClient
{
    public class DataGridMultiSelectionBehavior : Behavior<DataGrid>
    {
        public static readonly DependencyProperty SelectedItemsProperty =
            DependencyProperty.Register("SelectedItems", typeof(IList), typeof(DataGridMultiSelectionBehavior),
                new UIPropertyMetadata(null, SelectedItemsChanged));
 
        public IList SelectedItems
        {
            get => (IList)GetValue(SelectedItemsProperty);
            set => SetValue(SelectedItemsProperty, value);
        }
 
        private bool _isUpdatingTarget;
        private bool _isUpdatingSource;
 
        private static void SelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is DataGridMultiSelectionBehavior behavior))
            {
                return;
            }
 
            INotifyCollectionChanged newValue = e.NewValue as INotifyCollectionChanged;
 
            if (e.OldValue is INotifyCollectionChanged oldValue)
            {
                oldValue.CollectionChanged -= behavior.SourceCollectionChanged;
                behavior.AssociatedObject.SelectionChanged -= behavior.DataGridSelectionChanged;
            }
 
            if (newValue != null)
            {
                behavior.AssociatedObject.SelectedItems.Clear();
                foreach (object item in (IEnumerable)newValue)
                {
                    behavior.AssociatedObject.SelectedItems.Add(item);
                }
            }
 
            behavior.AssociatedObject.SelectionChanged += behavior.DataGridSelectionChanged;
            newValue.CollectionChanged += behavior.SourceCollectionChanged;
 
        }
        private void DataGridSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (_isUpdatingTarget)
            {
                return;
            }
 
            IList selectedItems = SelectedItems;
 
            if (selectedItems == null)
            {
                return;
            }
 
            try
            {
                _isUpdatingSource = true;
 
                foreach (object item in e.RemovedItems)
                {
                    selectedItems.Remove(item);
                }
 
                foreach (object item in e.AddedItems)
                {
                    selectedItems.Add(item);
                }
            }
            finally
            {
                _isUpdatingSource = false;
            }
        }
        private void SourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (_isUpdatingSource)
            {
                return;
            }
 
            try
            {
                _isUpdatingTarget = true;
 
                if (e.OldItems != null)
                {
                    foreach (object item in e.OldItems)
                    {
                        AssociatedObject.SelectedItems.Remove(item);
                    }
                }
 
                if (e.NewItems != null)
                {
                    foreach (object item in e.NewItems)
                    {
                        AssociatedObject.SelectedItems.Add(item);
                    }
                }
 
                if (e.Action == NotifyCollectionChangedAction.Reset)
                {
                    AssociatedObject.SelectedItems.Clear();
                }
            }
            finally
            {
                _isUpdatingTarget = false;
            }
        }
        protected override void OnAttached()
        {
            base.OnAttached();
            if (SelectedItems != null)
            {
                AssociatedObject.SelectedItems.Clear();
                foreach (object item in SelectedItems)
                {
                    AssociatedObject.SelectedItems.Add(item);
                }
            }
        }
    }
}
В XAML нужного окна добавляете пространство имён
Code
1
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
В нужном DataGrid подключаете созданный ранее Behavior и выставляете SelectionMode="Extended"
XML
1
2
3
4
5
6
<DataGrid x:Name="worksGrid" HorizontalAlignment="Stretch" ItemsSource="{Binding Works}" SelectedItem="{Binding SelectedWork}"
          SelectionMode="Extended">       
    <i:Interaction.Behaviors>
        <local:DataGridMultiSelectionBehavior SelectedItems="{Binding SelectedWorks}"/>
    </i:Interaction.Behaviors>
</DataGrid>
Во ViewModel этого окна создаёте свойство для SelectedItems
C#
1
public ObservableCollection<WorkDrawData> SelectedWorks { get; } = new ObservableCollection<WorkDrawData>();
И потом что хотите делаете с коллекцией SelectedItems.
1
1 / 1 / 0
Регистрация: 20.08.2021
Сообщений: 15
15.12.2021, 13:27  [ТС]
Andrey-MSK
Спасибо))) Именно в этом и трабл, только set не совсем понятно...
отпишусь, когда всё детально проработаю

Добавлено через 2 минуты
Добавлено через 2 часа 1 минуту
Элд Хасп,
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Комментарий модератора
Пока всего нИАсвоил))) но спасибо)
Цитата Сообщение от Элд Хасп Посмотреть сообщение
лучше переименовать команду в SetCommandAsync
Но это же не метод, а класс, инкапсулированные методы там правильный. ИЛИ? (вопрос принципиальный - много переименовывать придётся)
Цитата Сообщение от Элд Хасп Посмотреть сообщение
не рекомендую использовать асинхронные команды
источник данных там Web Api, говорят, не корректно синхронно, что логично - если связь плохая или данных много. хотя, похоже из-за этого сортировка иногда не корректно срабатывает:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
internal class SelectedComparer<TProxyEntity> : IComparer<Guid> 
    where TProxyEntity : ProxyEntity
{
    private readonly IList<TProxyEntity> _entities;
 
    public SelectedComparer(IList<TProxyEntity> entities) =>
        _entities = entities;
 
 
    public int Compare(Guid x, Guid y)
    {
        var hasX = _entities.Any(e => e.Id == x);
        var hasY = _entities.Any(e => e.Id == y);
        return hasX switch
        {
            true when !hasY => -1,
            false when hasY => 1,
            _ => 0
        };
    }
}
ЗЫ (по форому)
Почему у меня всё в один ответ лезит? (разным же персонам отвечаю)
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16124 / 11248 / 2888
Регистрация: 21.04.2018
Сообщений: 33,082
Записей в блоге: 2
15.12.2021, 13:41
Цитата Сообщение от VovikDoc99 Посмотреть сообщение
Но это же не метод, а класс, инкапсулированные методы там правильный. ИЛИ? (вопрос принципиальный - много переименовывать придётся)
Вопрос НЕ принципиальный.
И переделывать, если трудозатратно не стоит.
Но на будущее лучше задавать имена как принято.
Это облегчает взаимопонимание с другими программистами.

P.S. Вы знаете про быстрое переименование при рефакторинге по F2?
Добавлено через 6 минут
Цитата Сообщение от VovikDoc99 Посмотреть сообщение
источник данных там Web Api, говорят, не корректно синхронно, что логично - если связь плохая или данных много. хотя, похоже из-за этого сортировка иногда не корректно срабатывает:
Вы не поняли меня.
Я не говорил о том, что методы нужно вызывать синхронно.
Я только о самой команде.
Асинхронная команда это инкапсуляция некоторых действий при вызове из метода Execute целевого метода.
Совершается ряд скрытых действий как перед вызовом, так и после него.
И, при неполном понимании всей этой логики, может привести к трудноуловим багам.
Так как вы меня не поняли, то с большой вероятностью вы либо "в слепую" используете асинхронную команду по копипасте, либо у вас обычная команда вызывающая асинхронный метод и вы поэтому дали такое название и команде.

Добавлено через 1 минуту
Цитата Сообщение от VovikDoc99 Посмотреть сообщение
Почему у меня всё в один ответ лезит? (разным же персонам отвечаю)
Есть тайм-аут - большой, несколько часов, точно не проверял.
До его окончания все сообщения сливаются, если между ними нет сообщений от других пользователей, и если сообщения не содержат вложений.
1
1 / 1 / 0
Регистрация: 20.08.2021
Сообщений: 15
15.12.2021, 19:02  [ТС]
Элд Хасп,
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Совершается ряд скрытых действий как перед вызовом, так и после него.
у меня ничего скрытого нет:
C#
1
2
3
4
5
public interface IAsyncCommand<in T> : ICommand
{
    Task ExecuteAsync(T parameter);
    bool CanExecute(T? 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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
public abstract class AsyncCommandBase : IDisposable
{
    public event EventHandler? CanExecuteChanged;
 
    protected bool IsExecuting { get; set; }
    protected readonly IErrorHandler? ErrorCancelHandler;
    protected CancellationToken InnerCancellationToken { get; set; } = CancellationToken.None;
    protected CancellationTokenSource? ProxyCancellationTokenSource;
 
    protected AsyncCommandBase(IErrorHandler? errorCancelHandler,
         bool cancellationSupport, CancellationToken cancelToken)
    {
        if (cancellationSupport)
        {
            if (cancelToken == CancellationToken.None)
            {
                ProxyCancellationTokenSource = new CancellationTokenSource();
                InnerCancellationToken = ProxyCancellationTokenSource.Token;
            }
            else InnerCancellationToken = cancelToken;
        }
        ErrorCancelHandler = errorCancelHandler;
    }
 
    public void RaiseCanExecuteChanged() =>
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
 
    public bool IsNotCancelled 
    {
        get
        {
            CancelException();
            return !InnerCancellationToken.IsCancellationRequested;
        }
    }
 
    protected void CancelException()
    {
        if (InnerCancellationToken == CancellationToken.None)
            throw new NotSupportedException("Данная команда не поддерживает отмену.");
    }
 
    public void Cancel()
    {
        CancelException();
        if (ProxyCancellationTokenSource is null)
            throw new TaskCanceledException("Управляйте отменой внешним ресурсом (CancellationTokenSource).");
        if (ProxyCancellationTokenSource is { IsCancellationRequested: false })
            ProxyCancellationTokenSource.Cancel();
    }
 
    public void ResetCancel()
    {
        CancelException();
        switch (ProxyCancellationTokenSource)
        {
            case null:
                throw new TaskCanceledException(
                    "Управляйте отменой внешним ресурсом (CancellationTokenSource).");
            case { IsCancellationRequested: true }:
                ProxyCancellationTokenSource.Dispose();
                ProxyCancellationTokenSource = new();
                break;
        }
 
        InnerCancellationToken = ProxyCancellationTokenSource.Token;
    }
 
    public void ResetCancel(ref CancellationTokenSource newCancellationTokenSource)
    {
        CancelException();
        if (ProxyCancellationTokenSource is not null)
        {
            ProxyCancellationTokenSource.Dispose();
            ProxyCancellationTokenSource = null;
        }
 
        if (newCancellationTokenSource is { IsCancellationRequested: true })
        {
            newCancellationTokenSource.Dispose();
            newCancellationTokenSource = new();
        }
        InnerCancellationToken = newCancellationTokenSource.Token;
    }
 
    private bool _disposedValue;
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                ProxyCancellationTokenSource?.Dispose();
            }
 
            // TODO: освободить неуправляемые ресурсы (неуправляемые объекты) и переопределить метод завершения
            // TODO: установить значение NULL для больших полей
            _disposedValue = true;
        }
    }
 
    // // TODO: переопределить метод завершения, только если "Dispose(bool disposing)" содержит код для освобождения неуправляемых ресурсов
    // ~AsyncCommandBase()
    // {
    //     // Не изменяйте этот код. Разместите код очистки в методе "Dispose(bool disposing)".
    //     Dispose(disposing: false);
    // }
 
    public void Dispose()
    {
        // Не изменяйте этот код. Разместите код очистки в методе "Dispose(bool disposing)".
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }
}
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
public class AsyncCommand<T> : AsyncCommandBase, IAsyncCommand<T>
{
    private readonly Func<T?, CancellationToken, Task> _execute;
    private readonly Func<T?, bool>? _canExecute;
 
    public AsyncCommand(
        Func<T?, CancellationToken, Task> execute,
        Func<T?, bool>? canExecute = null,
        IErrorHandler? errorHandler = null,
        CancellationToken cancel = default)
        : base(errorHandler, true, cancel)
    {
        _execute = execute;
        _canExecute = canExecute;
    }
 
    public AsyncCommand(Func<T?, Task> execute,
        Func<T?, bool>? canExecute = null,
        IErrorHandler? errorHandler = null)
        : base(errorHandler, false, CancellationToken.None)
    {
        _execute = (p, _) => execute.Invoke(p);
        _canExecute = canExecute;
    }
 
    public bool CanExecute(T? parameter) =>
        !IsExecuting &&
        (_canExecute?.Invoke(parameter) ?? true) &&
        !InnerCancellationToken.IsCancellationRequested;
 
    public async Task ExecuteAsync(T? parameter)
    {
        if (CanExecute(parameter))
        {
            try
            {
                IsExecuting = true;
                await _execute.Invoke(parameter, InnerCancellationToken);
            }
            finally
            {
                IsExecuting = false;
            }
        }
        RaiseCanExecuteChanged();
    }
 
    #region Explicit implementations
    bool ICommand.CanExecute(object? parameter) => CanExecute((T?)parameter);
 
    void ICommand.Execute(object? parameter) =>
        ExecuteAsync((T?) parameter).FireAndForgetSafeAsync(ErrorCancelHandler);
    #endregion
}
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static async void FireAndForgetSafeAsync(this Task task, IErrorHandler? handler)
    {
        try
        {
            await task;
        }
        catch (OperationCanceledException ex)
        {
            if (handler is IErrorCancelHandler handlerWithCancel)
                handlerWithCancel.HandleCancel(ex);
            else handler?.HandleError(ex);
        }
        catch (ResultNotFoundException ex)
        {
            if (handler is IErrorNotFoundHandler handlerWithNotFound)
                handlerWithNotFound.HandleResultNotFound(ex);
            else handler?.HandleError(ex);
        }
        catch (Exception ex)
        {
            handler?.HandleError(ex);
        }
    }
PS
прошу прощения за много кода - послал самую общюю команду, просто попутно не перемудрил ли я?

Добавлено через 4 часа 4 минуты
Элд Хасп, "Билл Гейтс" пишет имена команд вообще без Async: async-programming-patterns-for-asynchronous-mvvm-applications-commands
0
1 / 1 / 0
Регистрация: 20.08.2021
Сообщений: 15
18.12.2021, 06:03  [ТС]
Andrey-MSK, Всё получилось даже лучше, чем ожидалось!
пара костылей ушло - тепеь во View остался один метод, к-ый не портит картины:
C#
1
2
3
4
5
private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (e.AddedItems.Count > 0 && sender is DataGrid grid)
        grid.ScrollIntoView(e.AddedItems[^1]!);
}
Ещё раз СПАСИБО)))
Только один вопросик остался, почему нужно использовать Behaviors, а не расширить DataGrig свойством WithSetSelectedItems?
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16124 / 11248 / 2888
Регистрация: 21.04.2018
Сообщений: 33,082
Записей в блоге: 2
18.12.2021, 07:25
Лучший ответ Сообщение было отмечено VovikDoc99 как решение

Решение

Цитата Сообщение от VovikDoc99 Посмотреть сообщение
Только один вопросик остался, почему нужно использовать Behaviors, а не расширить DataGrig свойством WithSetSelectedItems?
Behavior - это общепринятый способ добавления какого-то функционала DependecyObject.
Он входит в MS библиотеку и поэтому доверие к нему такое же как к .Net.
Чтение его кода и умение использовать, хотя не относится к базовым навыкам, всё же один из необходимых навыков для WPF программиста.
Так же в нём уже есть инкапсулированная логика значительно упрощающая его создание.

В данном случае, его без проблем моно заменить AP-свойством или сделать производный DataGrid с дополнительным DP-свойством.

P.S. У меня всё же большие сомнения в правильности вашей реализации.
Вы можете выложить полный код Решения - лучше всего через Гит?
И объясните зачем вам понадобилась коллекция SelectedItems в VM?
Вы совершаете с ней какие-то действия в реалтайм сразу при изменении выделения?
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
18.12.2021, 07:25
Помогаю со студенческими работами здесь

Какое событие возникает при потере фокуса в TextBox
как перехватить событие когда я чтота печатал в текстбоксе. а потом кликнул в другое место. кликед и ли что?

Какое событие возникает при раскрытии ветки в CTreeView?
Какое событие возникает при раскрытии ветки в CTreeView? И как узнать какая это ветка... MFC-приложение...

Какое событие возникает при выборе элемента в ComboBox
Какова лексика СomboBox в коде? Уже ввел возможные варианты в раскрывающийся список. Вот, нажимаю я дважды на него, попадаю в код. Что...

Какое возникает событие, если завершить процесс приложения
Доброго времени суток. Подскажите, какое возникает событие, если приложение завершит в процессах? Приложение консольное.

Какое событие возникает при разворачивании выпадающего списка?
Доброго времени суток! На форме комбобокс. Какое событие происходит при нажатии на значек (черная стрелочка), смотрите картинку....


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
SDL3 для Android: Загрузка PNG с альфа-каналом с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 28.01.2026
Содержание блога SDL3 имеет собственные средства для загрузки и отображения PNG-файлов с альфа-каналом и базовой работы с ними. В этой инструкции используется функция SDL_LoadPNG(), которая. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru