Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.82/34: Рейтинг темы: голосов - 34, средняя оценка - 4.82
0 / 0 / 0
Регистрация: 29.04.2024
Сообщений: 10

Взаимодействие UserControl, ContentControl и SelectedItem в Listbox

30.04.2024, 11:59. Показов 9072. Ответов 77

Студворк — интернет-сервис помощи студентам
Доброго утра!
Каждый элемент Listbox содержит путь к картинке, которая должна показываться в ContentControl с помощью свойства SelectedItem:
C#
1
2
3
4
5
6
private AgencyModel _selectedAgency;
public AgencyModel SelectedAgency
{
    get { return _selectedAgency; }
    set { Set(ref _selectedAgency, value); }
}
Окно MainWindow.xaml и UserControl HomeView используют одну ViewModel MainViewModel (реализует INotifyPropertyChanged).
В Listbox используется Expander.

Проблема в том, что SelectedItem ListBox никак не хочет коннектиться с HomeView, в котором отображается картинка. Хотя другие публичные свойства (не из ListBox) нормально биндятся в HomeView и отображаются в contentControl MainWindow.xaml.

Listbox размещен в MainWindow.xaml:

XML
1
2
3
4
5
6
7
<ListBox x:Name="list1" Grid.Column="0" Style="{x:Null}" BorderThickness="0"
         ItemsSource="{Binding Source={StaticResource ListBoxItems}, UpdateSourceTrigger=PropertyChanged}"
         SelectedItem="{Binding SelectedAgency, UpdateSourceTrigger=PropertyChanged}"                         
         ScrollViewer.HorizontalScrollBarVisibility="Hidden" Background="Transparent">
         <!--Здесь есть еще код для группировки, DataTemplate, стиль отображения-->
    
</ListBox>
ContentControl находится в MainWindow.xaml рядом с ListBox
XML
1
2
3
<ContentControl Grid.Column="1"
                 Margin="5"
                 Content="{Binding CurrentView, UpdateSourceTrigger=PropertyChanged}"/>
App.xaml:
XML
1
2
3
4
5
6
7
8
<Application.Resources>
        <ResourceDictionary>                   
            <DataTemplate DataType="{x:Type viewModel:HomeViewModel}">
                <view:HomeView/>
            </DataTemplate>
        </ResourceDictionary>
    </Application.Resources>
</Application>
HomeView.xaml
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<UserControl x:Class="WpfApp1.View.HomeView"
             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:WpfApp1.View"
             xmlns:MainVM="clr-namespace:WpfApp1.ViewModel"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="700"
             >
    <UserControl.DataContext>
        <MainVM:MainViewModel/>
    </UserControl.DataContext>
 
    <Grid Margin="0,0,0,0">
        <TextBlock Text="{Binding SettingM.MapFile}" Foreground="White"/>
        <Image Source="{Binding SelectedAgency.MapFile, UpdateSourceTrigger=PropertyChanged}" 
               Opacity="0.2" Stretch="Uniform"
               />
    </Grid>
</UserControl>
P.s. ListBox корректно взаимодействует с MainViewModel и свойством SelectedAgency.
Я так понимаю, что SelectedItem надо как то иначе обрабатывать, вопрос как это делать?
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
30.04.2024, 11:59
Ответы с готовыми решениями:

При выборе Item в одном ListBox'е, SelectedItem в другом ListBox, стал недоступен
А ещё скажите пожалуйста, как сделать, чтобы когда я выбирал Item в одном ListBox'е, Item в другом листбоксе, если до этого он был выбран,...

ListBox SelectedItem
Добрый день. Помогите пожалуйста с проблемкой : Есть: &lt;ListBox ItemsSource=&quot;{Binding Videos}&quot; SelectedItem=&quot;{Binding...

Убрать SelectedItem в ListBox
Добрый. Скажите пожалуйста, как убрать выделение item'a, после нажатие на него (см. вложение). Для чего мне это надо? - Дело в том, что...

77
0 / 0 / 0
Регистрация: 29.04.2024
Сообщений: 10
02.05.2024, 16:57  [ТС]
Студворк — интернет-сервис помощи студентам
Andrey-MSK, в перспективе использование DI следует изучить, но здесь и сейчас в моем случае статичный экземпляр класса проще использовать. Но, спасибо)
0
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
02.05.2024, 17:18
employee19, пример навигации можете изучить тут или тут в обоих случаях все работает без библиотек.
Про di(Dependency Injection) можете тут что то изучить но тут уже придется использовать библиотеку.
1
 Аватар для Andrey-MSK
3392 / 2278 / 388
Регистрация: 14.08.2018
Сообщений: 7,706
Записей в блоге: 4
02.05.2024, 17:26
Цитата Сообщение от xellan24rus Посмотреть сообщение
Про di(Dependency Injection) можете тут что то изучить но тут уже придется использовать библиотеку.
Ну тут уж совсем простенько... Как передавать параметры в новое окно, как получать из него результат и т.д. - нету самого главного
0
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
02.05.2024, 17:31
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
как получать из него результат и т.д. - нету самого главного
Согласен У меня нету опыта c di ибо использую все самописное: локаторы или навигаторы и т.п
Если у вас есть ссылки на примеры, то можете прикрепить их в сообщение, для автора вопроса это точно будет полезно и взято на заметку
0
 Аватар для Andrey-MSK
3392 / 2278 / 388
Регистрация: 14.08.2018
Сообщений: 7,706
Записей в блоге: 4
02.05.2024, 17:37
xellan24rus, Да там ничего сложного. Просто регистрировать и взаимодействовать с сервисами VM и View нужно через интерфейсы, в которых все нужные свойства и методы объявлены. Интерфейсы закинуть в какую-нибудь сборку, которая не будет ссылаться на платформозависимые ресурсы, а реализацию раскидать по нужным слоям, платформозависимые в слой View и в App всё связать. Покажу на своём проекте, напишу комментарии, но попозже
1
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
02.05.2024, 17:47
Andrey-MSK, я видел реализации в каких то исходниках, но примеры не найду уже. И di не использую, так как склоняюсь к изучению wpf и все реализации пишу без использования библиотек, чтобы во всём разобраться самому и знать как что чинить, в готовых решениях не всегда есть что хочется) К примеру material design для меня бесполезен ибо то что там есть меня не устраивает, пришлось написать свой проект с реализациями под себя
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Покажу на своём проекте, напишу комментарии, но попозже
Хорошо
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
02.05.2024, 20:35
Цитата Сообщение от xellan24rus Посмотреть сообщение
Конечно было бы классно использовать только свойство, а не всю vm. Но свойство не может реализовывать InotifyPropertyChanged, эта задача класса.
По подробнее...
Что имеете ввиду?
Что нельзя статические свойства автообновлять?
0
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
02.05.2024, 21:26
Элд Хасп, я пробовал сделать только свойство статическим, но это не дало результата в плане: чтобы в одном окне правил, а в другом свойство обновляло значение.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
02.05.2024, 21:34
Цитата Сообщение от xellan24rus Посмотреть сообщение
сделать только свойство статическим, но это не дало результата в плане: чтобы в одном окне правил, а в другом свойство обновляло значение.
Пример статической оболочки для часов
0
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
02.05.2024, 23:28
Элд Хасп, или я не понял или это не работает. Убрал лишний код с примера оболочки, оставил только свойства.
Есть vm с примерa для главного окна
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
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
 
namespace InstnceVM
{
    public class MainVM : INotifyPropertyChanged
    {
        public static MainVM Instanse { get; set; }
        public static string InstansData { get; set; }
        public MainVM()
        {
            Instanse = this;
            InstansData = Data;
        }
 
        private string _data;
 
        public string Data
        {
            get => _data;
            set
            {
                if (value == _data) return;
                _data = value;
                OnPropertyChanged();
            }
        }
 
        public event PropertyChangedEventHandler? PropertyChanged;
 
        protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
 
        protected bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(field, value)) return false;
            field = value;
            OnPropertyChanged(propertyName);
            return true;
        }
    }
}
и использование на странице у которой нет контекста
XML
1
2
3
4
5
   <StackPanel>
       <TextBlock Text="UserConrol" HorizontalAlignment="Center" Padding="10"/>
       <TextBox Text="{Binding Path=(local:MainVM.Instanse).Data, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="80"></TextBox>
       <TextBox Text="{Binding Path=(local:MainVM.InstansData), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="80"></TextBox>
   </StackPanel>
В первом TextBox используется и это обновляет на обоих view данные
C#
1
      public static MainVM Instanse { get; set; }
Во втором TextBox используется, и это не обновляет данные
C#
1
    public static string InstansData { get; set; }
Добавлено через 44 секунды
Значения задаю в конструкторе
0
 Аватар для Andrey-MSK
3392 / 2278 / 388
Регистрация: 14.08.2018
Сообщений: 7,706
Записей в блоге: 4
03.05.2024, 09:56
xellan24rus, employee19, Вот пример. Букав много, но оно того стоит
1. В сборке View создаём свойство для диалогов, которое будет возвращать результат нажатия кнопки Ok
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
using System.Windows;
 
namespace DBClient.Services
{
    public class DialogCloser
    {
        private static void DialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is Window window)
                window.DialogResult = e.NewValue as bool?;
        }
 
        public static readonly DependencyProperty DialogResultProperty =
            DependencyProperty.RegisterAttached(
                "DialogResult",
                typeof(bool?),
                typeof(DialogCloser),
                new PropertyMetadata(DialogResultChanged));
 
        public static void SetDialogResult(Window target, bool? value)
        {
            target.SetValue(DialogResultProperty, value);
        }
    }
}
2. В сервисной сборке, которая не зависит от платформы, создаём базовые интерфейсы для VM
C#
1
2
3
4
5
6
7
8
using System.ComponentModel;
 
namespace Services.Interfaces
{
    public interface IBaseViewModel : INotifyPropertyChanged
    {
    }
}
C#
1
2
3
4
5
6
7
namespace Services.Interfaces
{
    public interface IBaseDialogViewModel : IBaseViewModel
    {
        bool? DialogResult { get; set; }
    }
}
3. В сборке с VM создаём их реализацию
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
using System.ComponentModel;
using System.Runtime.CompilerServices;
 
using Services.Interfaces;
 
namespace VModels.Base
{
    public class BaseViewModel : INotifyPropertyChanged, IBaseViewModel
    {
        public event PropertyChangedEventHandler PropertyChanged;
 
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 
        protected virtual bool Set<T>(ref T field, T value, [CallerMemberName] string PropertyName = null)
        {
            if (Equals(field, value))
                return false;
 
            field = value;
 
            OnPropertyChanged(PropertyName);
 
            return true;
        }
    }
}
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using Services.Interfaces;
 
using VModels.Input;
 
namespace VModels.Base
{
    public abstract class BaseDialogViewModel : BaseViewModel, IBaseDialogViewModel
    {
        private bool? _dialogResult;
        public bool? DialogResult
        {
            get => _dialogResult;
            set => Set(ref _dialogResult, value);
        }
 
        private RelayCommand _saveCommand;
        public RelayCommand SaveCommand
            => _saveCommand ??= new RelayCommand(Save);
 
        protected abstract void Save(object obj);
    }
}
4. В сервисной сборке создаём интерфейсы сервисов, которые будут использоваться для взаимодействия с VM, окнами и т.д.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
using System;
using System.Threading.Tasks;
 
namespace Services.Interfaces
{
    // Сервис очереди выполнения асинхронных методов. Позволяет запускать их из конструктора и в свойствах
    public interface IBackgroundTaskQueue
    {
        void QueueBackgroundWorkItem(Func<Task> workItem);
        void Start();
        void Stop();
    }
}
C#
1
2
3
4
5
6
7
8
namespace Services.Interfaces
{
    // Сервис для поучения VM из регистрации DI
    public interface IViewModelFactory
    {
        T CreateViewModel<T>() where T : IBaseViewModel;
    }
}
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
namespace Services.Interfaces
{
    // Сервис управления окнами
    public interface IWindowService
    {
        void ShowErrorMessage(string message, string caption);
        void ShowInfoMessage(string message, string caption);
        bool ShowQuestionMessage(string message, string caption);
        bool ShowWarningMessage(string message, string caption);
        void ShowWindow(IBaseViewModel viewModel);
        bool? ShowDialog(IBaseViewModel viewModel);
    }
}
5. В сборке View создаём интерфейс сервиса связи окон и VM
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System.Windows;
using System.Windows.Controls;
 
using Services.Interfaces;
 
namespace DBClient.Services.Interfaces
{
    public interface IViewModelsService
    {
        Window GetWindow(IBaseViewModel viewModel);
        ContentControl GetControl(IBaseViewModel viewModel);
        IViewModelsService Register<TViewModelType, TViewType>()
            where TViewType : ContentControl, new()
            where TViewModelType : IBaseViewModel;
    }
}
И его реализация в этой же сборке
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
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
 
using DBClient.Services.Interfaces;
 
using Services.Interfaces;
 
namespace DBClient.Services
{
    public class ViewModelsService : IViewModelsService
    {
        private readonly Dictionary<Type, Func<ContentControl>> _dict = new Dictionary<Type, Func<ContentControl>>();
 
        public IViewModelsService Register<TViewModelType, TViewType>()
            where TViewType : ContentControl, new()
            where TViewModelType : IBaseViewModel
        {
            _dict[typeof(TViewModelType)] = () => new TViewType();
 
            return this;
        }
 
        public Window GetWindow(IBaseViewModel viewModel)
        {
            var result = GetControl(viewModel);
 
            if (result is not Window window)
                throw new Exception($"{result.GetType()} is not window");
 
            return window;
        }
 
        public ContentControl GetControl(IBaseViewModel viewModel)
        {
            var parentTypes = GetParentTypes(viewModel.GetType());
 
            foreach (var parentType in parentTypes)
            {
                if (_dict.TryGetValue(parentType, out var res))
                    return res();
            }
 
            throw new Exception($"ViewModel {viewModel.GetType().Name} is not registered");
        }
 
        private static IEnumerable<Type> GetParentTypes(Type type)
        {
            // is there any base type?
            if (type == null)
                yield break;
 
            yield return type;
 
            // return all implemented or inherited interfaces
            foreach (var i in type.GetInterfaces())
                yield return i;
 
            // return all inherited types
            var currentBaseType = type.BaseType;
 
            while (currentBaseType != null)
            {
                yield return currentBaseType;
                currentBaseType = currentBaseType.BaseType;
            }
        }
    }
}
Тут же содаём реализации IViewModelFactory и IWindowService
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System;
 
using Microsoft.Extensions.DependencyInjection;
 
using Services.Interfaces;
 
namespace DBClient.Services
{
    public class ViewModelFactory : IViewModelFactory
    {
        private readonly IServiceProvider _serviceProvider;
 
        public ViewModelFactory(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
 
        public T CreateViewModel<T>() where T : IBaseViewModel
            => _serviceProvider.GetService<T>();
    }
}
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
using System;
using System.IO;
using System.Linq;
using System.Windows;
 
using DBClient.Services.Interfaces;
 
using Microsoft.Win32;
 
using Services.Interfaces;
 
using VModels.WindowsVM;
 
namespace DBClient.Services
{
    internal class WindowService : IWindowService
    {
        private readonly IViewModelsService _viewModelsService;
 
        public WindowService(IViewModelsService viewModelsService)
        {
            _viewModelsService = viewModelsService;
        }
 
        public Window GetWindow(IBaseViewModel viewModel)
        {
            var window = _viewModelsService.GetWindow(viewModel);
 
            window.DataContext = viewModel;
 
            return window;
        }
 
        public void ShowWindow(IBaseViewModel viewModel)
            => GetWindow(viewModel).Show();
 
        public bool? ShowDialog(IBaseViewModel viewModel)
            => GetWindow(viewModel).ShowDialog();
 
        public void ShowErrorMessage(string message, string caption)
            => MessageBox.Show(message, caption, MessageBoxButton.OK, MessageBoxImage.Error);
 
        public void ShowInfoMessage(string message, string caption)
            => MessageBox.Show(message, caption, MessageBoxButton.OK, MessageBoxImage.Information);
 
        public bool ShowQuestionMessage(string message, string caption)
        {
            if (MessageBox.Show(message, caption, MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
                return true;
            else
                return false;
        }
 
        public bool ShowWarningMessage(string message, string caption)
        {
            if (MessageBox.Show(message, caption, MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
                return true;
            else
                return false;
        }
    }
}
Добавлено через 27 секунд
xellan24rus, employee19,
6. В классе Application запускаем всё это дело, предварительно установив из NuGet
C#
1
2
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
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
namespace DBClient
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        // Коллекция наших сервисов DI
        public IServiceProvider Services { get; }
        // Конфигурация нашего приложения
        public IConfiguration Configuration { get; }
 
        // Объект нашего приложения
        public static new App Current => (App)Application.Current;
 
        public App()
        {
            Configuration = GetConfiguration();
            Services = ConfigureServices();
 
            InitializeComponent();
        }
 
        // Загружаем конфигурацию
        private static IConfiguration GetConfiguration()
        {
            IConfigurationBuilder builder = new ConfigurationBuilder()
                .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
                .AddJsonFile("appsettings.json");
 
            return builder.Build();
        }
 
        // Запускаем сервисы
        private static IServiceProvider ConfigureServices()
        {
            ServiceCollection services = new ServiceCollection();
 
            services
                .AddSingleton(Current.Configuration)
                .AddSingleton<IViewModelsService, ViewModelsService>()
                .AddSingleton<IWindowService, WindowService>()
                .AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>()
                .AddSingleton<IViewModelFactory, ViewModelFactory>();
 
            return services.BuildServiceProvider();
        }
    }
}
7. Начинаем создавать VM. Покажу на примере диалога, так как будет видна передача данных в обе стороны
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System.Threading;
 
using DTOClassLibrary.DTO;
 
namespace Services.Interfaces.DialogsVM
{
    public interface ICPSContractEditDialogVM : IBaseDialogViewModel
    {
        // Свойства для взаимодействия МЬ с остальным кодом приложения
        CPSContract Contract { get; set; }
        bool? EditMode { get; set; }
        bool? IsLoaded { get; set; }
        SynchronizationContext ViewContext { get; set; }
    }
}
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
 
using DALibrary.Interfaces;
 
using DTOClassLibrary.DTO;
 
using Services.Interfaces;
using Services.Interfaces.DialogsVM;
 
using VModels.Base;
 
namespace VModels.DialogsVM
{
    // Наследуемся от базавого класса и подключаем интерфейс для взамодействия
    public class CPSContractEditDialogVM : BaseDialogViewModel, ICPSContractEditDialogVM
    {
        #region Private ViewModel members region
 
        private readonly IWindowService _windowService;
        private readonly IBackgroundTaskQueue _taskQueue;
        private readonly IMainDA _mainDA;
 
        #endregion
 
        #region ViewModel properties for binding UI elements
 
        private CPSContract _contract;
        private bool? _editMode;
        private DateTime? _selectedContractDate;
        private string _contractNumber = string.Empty;
        private string _contractName = string.Empty;
        private string _contractType = string.Empty;
        private string _selectedContractType;
        private bool? _isLoaded;
 
        public SynchronizationContext ViewContext { get; set; }
        public CPSContract Contract
        {
            get => _contract;
            set => Set(ref _contract, value);
        }
        public bool? EditMode
        {
            get => _editMode;
            set => Set(ref _editMode, value);
        }
        public DateTime? SelectedContractDate
        {
            get => _selectedContractDate;
            set => Set(ref _selectedContractDate, value);
        }
        public string ContractNumber
        {
            get => _contractNumber;
            set => Set(ref _contractNumber, value);
        }
        public string ContractName
        {
            get => _contractName;
            set => Set(ref _contractName, value);
        }
        public string ContractType
        {
            get => _contractType;
            set => Set(ref _contractType, value);
        }
        public string SelectedContractType
        {
            get => _selectedContractType;
            set => Set(ref _selectedContractType, value);
        }
        public List<string> ContractTypes { get; } = new List<string>();
        public bool? IsLoaded
        {
            get => _isLoaded;
            set
            {
                if (Set(ref _isLoaded, value))
                {
                    if (IsLoaded == true)
                    {
                        switch (EditMode)
                        {
                            case true:
                                SelectedContractDate = Contract.ContractDate;
                                ContractNumber = Contract.ContractNumber;
                                ContractName = Contract.ContractName;
                                ContractType = Contract.ContractType;
 
                                _taskQueue.QueueBackgroundWorkItem(LoadVMData);
                                break;
 
                            case false:
                                Contract = new CPSContract();
 
                                _taskQueue.QueueBackgroundWorkItem(LoadVMData);
                                break;
                        }
                    }
                }
            }
        }
 
        #endregion
 
        #region RelayCommand region
 
        #endregion
 
        #region Methods region
 
        // Инжектим нужные сервисы в конструктор VM с помощью DI
        public CPSContractEditDialogVM(IWindowService windowService,
                                       IBackgroundTaskQueue taskQueue,
                                       IMainDA mainDA)
        {
            _windowService = windowService;
            _taskQueue = taskQueue;
            _mainDA = mainDA;
        }
 
        // Метод который отрабатывает при нажатии кнопки Ok
        protected override void Save(object obj)
        {
            if (SelectedContractDate != null && !string.IsNullOrEmpty(ContractNumber) && !string.IsNullOrEmpty(ContractName) && !string.IsNullOrEmpty(ContractType))
            {
                Contract.ContractDate = SelectedContractDate.Value.Date;
                Contract.ContractNumber = ContractNumber;
                Contract.ContractName = ContractName;
                Contract.ContractType = ContractType;
 
                DialogResult = true;
            }
            else
            {
                _windowService.ShowErrorMessage("Все поля должны быть заполнены!", "Ошибка");
            }
        }
 
        private async Task LoadVMData()
        {
            try
            {
                List<string> strings = await _mainDA.CPSDataDA.GetContractTypesAsync();
 
                ViewContext.Send(vc =>
                {
                    if (ContractTypes.Count > 0)
                        ContractTypes.Clear();
 
                    strings.ForEach(s => ContractTypes.Add(s));
                }, null);
            }
            catch (Exception ex)
            {
                _windowService.ShowErrorMessage(ex.Message, "Ошибка");
            }
        }
 
        #endregion
    }
}
8. Создаём View нужного диалога
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
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
<Window x:Class="DBClient.Views.Dialogs.CPSContractEditDialog"
        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:i="http://schemas.microsoft.com/xaml/behaviors"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DBClient.Views.Dialogs"
        xmlns:services="clr-namespace:DBClient.Services" 
        xmlns:behaviors="clr-namespace:DBClient.Behaviors"
        xmlns:dialogsvm="clr-namespace:VModels.DialogsVM;assembly=VModels" 
        d:DataContext="{d:DesignInstance Type=dialogsvm:CPSContractEditDialogVM}"
        mc:Ignorable="d"
        Title="Редактирование договора" Height="300" Width="600"
        WindowStartupLocation="CenterScreen"
        <!-- Подключаем обработку кнопки Ok -->
        services:DialogCloser.DialogResult="{Binding DialogResult}" 
        Loaded="Window_Loaded">
 
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
 
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
 
        <Border Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
                Background="LightSteelBlue" CornerRadius="2" Margin="2">
            <TextBlock Text="Редактирование договора" FontSize="18" FontWeight="Bold" Margin="5"/>
        </Border>
 
        <Border Grid.Row="1" Grid.Column="0" BorderBrush="DarkGray" 
                BorderThickness="2" CornerRadius="2" Margin="2 0 2 2">
 
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
 
                <TextBlock Grid.Row="0" Grid.Column="0" Text="Дата"
                           Margin="2 2 2 0" Padding="2" HorizontalAlignment="Left"/>
                <DatePicker Grid.Row="1" Grid.Column="0" Margin="2"
                            HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                            VerticalContentAlignment="Center"
                            SelectedDate="{Binding SelectedContractDate}"
                            IsTodayHighlighted="True"/>
                <TextBlock Grid.Row="0" Grid.Column="1" Text="Номер"
                           Margin="2 2 2 0" Padding="2" HorizontalAlignment="Left"/>
                <TextBox Grid.Row="1" Grid.Column="1" Margin="2" Padding="4"
                         HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                         HorizontalContentAlignment="Left" VerticalContentAlignment="Center"
                         Text="{Binding ContractNumber}"
                         x:Name="txtNumber">
                    <i:Interaction.Behaviors>
                        <behaviors:SelectAllTextOnFocusBehavior/>
                    </i:Interaction.Behaviors>
                </TextBox>
                <TextBlock Grid.Row="0" Grid.Column="2" Text="Тип"
                           Margin="2 2 2 0" Padding="2" HorizontalAlignment="Left"/>
                <ComboBox Grid.Row="1" Grid.Column="2" Margin="2" Padding="2"
                          IsReadOnly="False" IsEditable="True"
                          ItemsSource="{Binding ContractTypes}"
                          SelectedItem="{Binding SelectedContractType}"
                          Text="{Binding ContractType}"/>
                <TextBlock Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3"
                           Text="Наименование" Margin="2 2 2 0" Padding="2"
                           HorizontalAlignment="Left"/>
                <TextBox Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3"
                         Margin="2" Padding="2"
                         HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                         HorizontalContentAlignment="Left" TextWrapping="Wrap"
                         Text="{Binding ContractName}">
                    <i:Interaction.Behaviors>
                        <behaviors:SelectAllTextOnFocusBehavior/>
                    </i:Interaction.Behaviors>
                </TextBox>
            </Grid>
        </Border>
 
        <Border Grid.Row="1" Grid.Column="1" Background="LightSteelBlue" 
                CornerRadius="2" Margin="0 0 2 2">
            <StackPanel Orientation="Vertical" VerticalAlignment="Bottom">
                <Button Content="Сохранить" Width="125" Height="25"
                        Margin="5" VerticalContentAlignment="Center"
                        Command="{Binding SaveCommand}"/>
                <Button Content="Отмена" Width="125" Height="25"
                        Margin="5 0 5 5" VerticalContentAlignment="Center" IsCancel="True"/>
            </StackPanel>
        </Border>
    </Grid>
</Window>
9. Подключаем всё это дело к DI
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Запускаем сервисы
private static IServiceProvider ConfigureServices()
{
    ServiceCollection services = new ServiceCollection();
 
    services
        .AddSingleton(Current.Configuration)
        .AddSingleton<IViewModelsService, ViewModelsService>()
        .AddSingleton<IWindowService, WindowService>()
        .AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>()
        .AddSingleton<IViewModelFactory, ViewModelFactory>()
        // Запускаем VM как сервис по требованию
        .AddTransient<IMainWindowVM, MainWindowVM>()
        .AddTransient<ICPSContractEditDialogVM, CPSContractEditDialogVM>();
 
    return services.BuildServiceProvider();
}
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
// Переопределение базового метода Application
protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);
 
    // Получаем нужные сервисы мз DI
    IViewModelsService _viewModelsService
        = Current.Services.GetService<IViewModelsService>();
    IBackgroundTaskQueue _taskQueue
        = Current.Services.GetService<IBackgroundTaskQueue>();
    
    // Связываем VM и окна
    _viewModelsService
        .Register<MainWindowVM, MainWindow>()
        .Register<CPSContractEditDialogVM, CPSContractEditDialog>();
 
    // Запускаем сервис очереди асинхронных методов
    _taskQueue.Start();
 
    // Подключаем VM главного окна
    MainWindow = new MainWindow { DataContext = Current.Services.GetService<IMainWindowVM>() };
    // Показываем главное окно
    MainWindow.Show();
}
10. Ну наконец использование этого дела для взаимодействия между VM
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
// Метод в VM
private async Task AddContractAsync()
{
    try
    {
        // Получаем VM из сервиса
        var vm = _viewModelFactory.CreateViewModel<ICPSContractEditDialogVM>();
 
        // Устанавливаем свойства
        vm.EditMode = false;
 
        // Показываем диалог
        _windowService.ShowDialog(vm);
 
        // Если Ok
        if (vm.DialogResult == true)
        {
            // Получаем данные из диалога
            vm.Contract.EObjectID = CurrentEObject.EObjectID;
            vm.Contract.BranchID = (int)Branch;
 
            // И что-то с ними делаем
            await _mainDA.CPSDataDA.AddContractAsync(vm.Contract);
 
            ViewContext.Send(vc =>
            {
                Contracts.Add(vm.Contract);
            }, null);
 
            SelectedContract = Contracts.FirstOrDefault(c => c.ContractID == vm.Contract.ContractID);
        }
    }
    catch (Exception ex)
    {
        _windowService.ShowErrorMessage(ex.Message, "Ошибка");
    }
}
Вот примерно так
2
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
03.05.2024, 12:01
xellan24rus, ответ в этой теме: Статические свойства с автообновлением представления
1
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
03.05.2024, 17:02
Элд Хасп, спасибо) вот так вот сделал, теперь нет нужды ради одного свойства делать инстанс всей vm)
Но в коде инстанс всей vm и отдельного свойства, стирать ничего не стал.
Кликните здесь для просмотра всего текста
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
 public class MainVM : INotifyPropertyChanged
  {
      public static MainVM Instanse { get; set; }
      private static string? _InstansData;
      public static string? InstansData
      {
          get => _InstansData;
          set
          {
              _InstansData = value;
              StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(nameof(InstansData)));
              //RaiseStaticPropertyChanged();
          }
      }
      public MainVM()
      {
          Instanse = this;
      }
 
      private string _data;
 
      public string Data
      {
          get => _data;
          set
          {
              _data = value;
              MainVM.InstansData = value;
              OnPropertyChanged();
          }
      }
 
      #region Статическое обновление свойства
 
      public static event EventHandler<PropertyChangedEventArgs>? StaticPropertyChanged;
      private static void RaiseStaticPropertyChanged([CallerMemberName] string? propertyName = null)
      {
          StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
      }
 
      #endregion
 
      public event PropertyChangedEventHandler? PropertyChanged;
 
      protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
      {
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }
 
      protected bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
      {
          if (EqualityComparer<T>.Default.Equals(field, value)) return false;
          field = value;
          OnPropertyChanged(propertyName);
          return true;
      }
  }


Добавлено через 5 минут
Andrey-MSK, спасибо за пример) А исходник есть такой?) В нем разобраться быстрее
0
 Аватар для Andrey-MSK
3392 / 2278 / 388
Регистрация: 14.08.2018
Сообщений: 7,706
Записей в блоге: 4
06.05.2024, 08:33
Цитата Сообщение от xellan24rus Посмотреть сообщение
А исходник есть такой?
Дак я же всё основное показал... Всё остальное ограничено вашей фантазией
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
C#
1
namespace DBClient
Сборка с View и Application.
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
C#
1
namespace Services
Сборка со всякими вспомогательными вещами, от платформы не зависит.
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
C#
1
namespace VModels
Сборка с VM. К платформе привязана, так как используется класс CommandManager для работы RelayCommand.
1
0 / 0 / 0
Регистрация: 29.04.2024
Сообщений: 10
06.05.2024, 10:34  [ТС]
xellan24rus, Andrey-MSK, не подскажите, где или как подсунуть SelectedItem, который в ListBox, нужное мне значение, дефолтное, вроде noname или вообще null. Ведь сразу после показа окна значение в нем устанавливается, равное первому из ListBox.

Upd, в Window_Loaded присвоил SelectedIndex = -1
0
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
06.05.2024, 21:43
employee19, для SelectedItem вы задаете в vm свойство, при загрузке просто укажите нужное значение в для привязанного свойства у SelectedItem.
Примерно так
tackPanel>
XML
1
2
3
4
5
            <ListBox x:Name="listBoxImages" SelectedItem="{Binding Selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                <ListBoxItem>1.jpeg</ListBoxItem>
                <ListBoxItem>Images/image2.jpg</ListBoxItem>
                <ListBoxItem>Images/image3.jpg</ListBoxItem>
            </ListBox>
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
   public class HomeView : INPC
   {
       private string _Selected;
       public string Selected
       {
           get => _Selected;
           set
           {
               _Selected = value;
               RaisePropertyChanged(nameof(SelectedAgency));
           }
       }
       public HomeView()
       {
           Selected = "Какие то данные";
       }
   }
Цитата Сообщение от employee19 Посмотреть сообщение
в Window_Loaded присвоил SelectedIndex = -1
Идекс начинается всегда с нуля. Если вы задаете -1 это значит что ничего выбрано не будет. И использовать в паре SelectedIndex и SelectedItem не имеет смысла ибо это не будет работать корректно. Для работы всегда выбирается что то одно или значения ил индексы.
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3461 / 2782 / 575
Регистрация: 04.09.2018
Сообщений: 8,746
Записей в блоге: 3
06.05.2024, 21:54
Цитата Сообщение от xellan24rus Посмотреть сообщение
"{Binding Selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
UpdateSourceTrigger здесь очевидно лишний, т.к. при "тычке" на элементе списка сигнал и так генерируется.
Цитата Сообщение от xellan24rus Посмотреть сообщение
И использовать в паре SelectedIndex и SelectedItem не имеет смысла
Тут тоже не совсем понял. Индекс это индекс, а итем - это итем, и они очень хорошо живут друг с другом. Индекс указывает на текущий выделенный итем, а итем может быть чем угодно...
0
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
06.05.2024, 22:15
Цитата Сообщение от wizard41 Посмотреть сообщение
Тут тоже не совсем понял
К примеру можно испытать подобные проблемы есть и другие примеры когда использование их в связке только ухудшает работу.
Вот пример

При этом если удалить SelectedIndex="0"

То отображается корректно
Цитата Сообщение от wizard41 Посмотреть сообщение
и они очень хорошо живут друг с другом.
явление этой проблемы обсуждалось много где. Причину не опишу, но чтобы не наткнуться на такое, лучше не использовать эту парочку вместе.
Цитата Сообщение от wizard41 Посмотреть сообщение
UpdateSourceTrigger здесь очевидно лишний, т.к. при "тычке" на элементе списка сигнал и так генерируется.
Согласен дал лишка в коде
0
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
06.05.2024, 22:16
В примере указаны одни и те же данные, так что по логике они имеют одну ссылку на источник
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3461 / 2782 / 575
Регистрация: 04.09.2018
Сообщений: 8,746
Записей в блоге: 3
06.05.2024, 22:26
xellan24rus, если исходить из того, что OnPropertyChanged нужен только для обновления View, то сам View их генерит самостоятельно, т.к. мы, собственно, по этим элементам сами "стучим".
Цитата Сообщение от xellan24rus Посмотреть сообщение
явление этой проблемы обсуждалось много где
Мммм... Ну может быть. Интересно, надо исследовать. Я пока с таким не встречался.
Цитата Сообщение от xellan24rus Посмотреть сообщение
если удалить SelectedIndex="0"
Так его не должно быть в XAML'е, если мы привязались в свойству Index в VM...

Добавлено через 5 минут
Цитата Сообщение от xellan24rus Посмотреть сообщение
по логике они имеют одну ссылку на источник
но они друг другу не мешают, по логике. Если в VM изначально будет индекс 0, то он и укажет на 0-ой элемент списка итемов.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
06.05.2024, 22:26

SelectedItem не работает у ListBox
Здравствуйте, SelectedItem не возвращает значение выбранного элемента, отсюда этот элемент я не могу удалить из списка. ListBox у меня...

Не работает SelectedItem Binding в ListBox
Использую MVVM. Есть ListBox: &lt;ListBox HorizontalContentAlignment=&quot;Stretch&quot; SelectedItem=&quot;{Binding SelectedUser}&quot;...

Сброс значения ListBox SelectedItem
Всем привет. Есть код: MyImages p = listBox.SelectedItem as MyImages; if (p != null) ...

SelectedItem в listbox последнего элемента
как при добавлении в Listbox элемента, именно на нем было выделение. Т.е. выделялся последний добавленный в список элемент. Используется...

listBox.SelectedItem - обращение к несуществующему объекту
Ошибка: Object reference not set to an instance of an object. говорит о попытке сослаться на несуществующий в момент обращения к нему...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Запись в регистр сведений независимо от заполненности табличной части
Maks 25.08.2026
Реализация из решения ниже выполнена на нетиповом документе с несколькими табличными частями, разработанного в КА2. Задача: Обеспечить запись документа в регистр сведений независимо от. . .
Ноутбук Альфария
kumehtar 24.08.2026
Встретился тут в сети ноутбук Альфария, примарха Альфа-Легиона. Хотя возможно, это ноутбук Омегона, разумеется. Ну как вам?
Мастера простых решений
DevAlt 23.08.2026
В сишарп стэках winforms, да и wpf существует сложная система связывания источниках данных и элементов формы(текстовые поля и метки), опирается все это на технологию событий и мета. . .
Цена ошибки
DevAlt 23.08.2026
Человек я беспокойный и потому заинтересовался OCaml, в чате форсили функторы модулей как суперфичу. Пытаясь отдуплить концепт, наткнулся на тутор с простым примером. А главный принцип обучения от. . .
Сегодня суббота, 22.08.2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины.
zorxor 22.08.2026
Сегодня суббота, 22. 08. 2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины. Кто Я, откуда Я пришел и куда Я иду? Эти вопросы не оставляют меня ни на секунду. Жизнь на планете Земля. . .
Жизня: рисунок укладки багажа, сделанный клодом
anaschu 21.08.2026
Сделал 15 снимков, он по снимкам сделал схему.
Был там один разговор по поводу свободы в материальном мире.
kumehtar 19.08.2026
Суть: рассматривается живое существо, оказавшееся внутри довольно странной системы (этого мира) и пытающееся обустроить в ней свой кусок пространства. Жизнь действительно предъявляет каждому. . .
Когда логика программы не спасает от человеческих ошибок
Maks 18.08.2026
В последнее время всё чаще и чаще сталкиваюсь с таким явлением, как абсолютная невнимательность (или глупость) пользователей. Проявляется это чаще всего на работе в коллективе. Допустим, человек с. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru