Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.66/218: Рейтинг темы: голосов - 218, средняя оценка - 4.66
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2

Пример реализации WPF+MVVM приложения

26.12.2018, 06:04. Показов 45371. Ответов 45

Студворк — интернет-сервис помощи студентам
Тема из цикла Готовые решения, примеры и рекомендации начинающим на WPF [Элд Хасп]

Пример решения ТЗ по теме Нечеткий поиск
В теме поставлена задача реализовать: поиск слов по списку слов, отображение списка слов, дополнение списка слов.

В любом решении WPF используются интерфейсы ICommand для команд и интерфейс INotifyPropertyChanged (сокращёно INPC) для свойств ViewModel. Поэтому в моих решениях всегда есть два класса:
RelayCommand - реализующий интерфейс ICommand
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
    public class RelayCommand : ICommand
    {
        public event EventHandler CanExecuteChanged;
        private readonly Func<object, bool> _canExecute;
        private readonly Action<object> _onExecute;
 
        /// <summary>Конструктор команды</summary>
        /// <param name="execute">Выполняемый метод команды</param>
        /// <param name="canExecute">Метод разрешающий выполнение команды</param>
        public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
        {
            _onExecute = execute;
            _canExecute = canExecute;
        }
 
        /// <summary>Вызов разрешающего метода команды</summary>
        /// <param name="parameter">Параметр команды</param>
        /// <returns>True? если выполнение команды разрешено</returns>
        public bool CanExecute(object parameter) => _canExecute == null ? true : _canExecute.Invoke(parameter);
 
        /// <summary>Вызов выполняющего метода команды</summary>
        /// <param name="parameter">Параметр команды</param>
        public void Execute(object parameter) => _onExecute?.Invoke(parameter);
    }
OnPropertyChangedClass - реализующий интерфейс INotifyPropertyChanged
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
    /// <summary>Базовый класс с реализацией INPC </summary>
    public class OnPropertyChangedClass : INotifyPropertyChanged
    {
        /// <summary>Событие для извещения об изменения свойства</summary>
        public event PropertyChangedEventHandler PropertyChanged;
 
        /// <summary>Метод для вызова события извещения об изменении свойства</summary>
        /// <param name="prop">Изменившееся свойство или список свойств через разделители "\\/\r \n()\"\'-"</param>
        public void OnPropertyChanged([CallerMemberName]string prop = "")
        {
            string[] names = prop.Split("\\/\r \n()\"\'-".ToArray(), StringSplitOptions.RemoveEmptyEntries);
            if (names.Length != 0)
                foreach (string _prp in names)
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(_prp));
        }
 
        /// <summary>Метод для вызова события извещения об изменении списка свойств</summary>
        /// <param name="propList">Последовательность имён свойств</param>
        public void OnPropertyChanged(IEnumerable<string> propList)
        {
            foreach (string _prp in propList.Where(name => !string.IsNullOrWhiteSpace(name)))
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(_prp));
        }
 
        /// <summary>Метод для вызова события извещения об изменении списка свойств</summary>
        /// <param name="propList">Последовательность свойств</param>
        public void OnPropertyChanged(IEnumerable<PropertyInfo> propList)
        {
            foreach (PropertyInfo _prp in propList)
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(_prp.Name));
        }
 
        /// <summary>Метод для вызова события извещения об изменении всех свойств</summary>
        public void OnAllPropertyChanged() => OnPropertyChanged(GetType().GetProperties());
    }
В паттерне MVVM создание приложения начинается с создания Model (Модели). В нашем случае в Модели должны быть реализованы методы и свойство:
  • Чтение списка слов из файла
  • Сохранение списка слов в файле
  • Пополнение списка слов
  • Поиск по списку слов с возвратом результата поиска
  • Свойство возвращающие коллекцию из списка слов. Так как коллекция предназначена для привязки к WPF элементу, то она должна поддерживать интерфейс INotifyCollectionChanged (сокращёно INCC).
Во многих случаях для Модели удобно выбрать статический класс. Так же сделаем и для этого решения.
Класс Модели - FuzzySearch_Model
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
    /// <summary>Статический класс модели данных</summary>
    public static class FuzzySearch_Model
    {
        static ObservableCollection<string> _listWords;
        /// <summary>Список слов</summary>
        public static ObservableCollection<string> ListWords
        {
            get => _listWords ?? (_listWords = new ObservableCollection<string>());
            private set => _listWords = value;
        }
 
        /// <summary>Сохранение списка слов в файле</summary>
        /// <param name="NameFile">Имя файла</param>
        public static void Save(string NameFile = null)
        {
            if (string.IsNullOrWhiteSpace(NameFile))
                NameFile = "ListWords.txt";
            StreamWriter file = null;
            try
            {
                file = new StreamWriter(NameFile, false, Encoding.Default);
                foreach (string word in ListWords)
                    file.WriteLine(word.ToLower());
            }
            catch (Exception)
            {
                MessageBox.Show("Ошибка записи в файл!");
            }
            if (file != null)
                file.Close();
        }
 
        /// <summary>Чтение списка слов из файла</summary>
        /// <param name="NameFile">Имя файла</param>
        public static void Load(string NameFile = null)
        {
            if (string.IsNullOrWhiteSpace(NameFile))
            {
                NameFile = "ListWords.txt";
                if (!File.Exists(NameFile))
                    File.Create(NameFile).Close();
            }
            StreamReader file = null;
            try
            {
                file = new StreamReader(NameFile, Encoding.Default);
                ListWords = new ObservableCollection<string>(file.ReadToEnd().Split("\r\n ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Select(wrd => wrd.ToLower()));
            }
            catch (Exception)
            {
                MessageBox.Show("Ошибка чтения из файла!");
            }
            if (file != null)
                file.Close();
        }
 
        /// <summary>Добавление слова в список</summary>
        /// <param name="Words">Слово или список слов разделённых переносом строки или пробелом</param>
        public static void AddWord(string Words)
        {
            foreach (string word in Words.Split("\r\n ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
                ListWords.Add(word);
        }
 
        /// <summary>Поиск слова в списке</summary>
        /// <param name="Word">Одно слово</param>
        /// <returns>Результат поиска</returns>
        public static string SearchWord(string Word)
        {
            string[] wordArr = Word.ToLower().Split("\r\n ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            if (wordArr.Length != 1) return "Ошибка ввода";
            Word = wordArr[0];
            int index = ListWords.IndexOf(Word);
            if (index >= 0) return "Tочное совпадение со словом по индексу " + index;
            /// *************************
            /// Нечёткий поиск
            /// *************************
            return "Результат нечёткого поиска";
        }
    }
После создания Модели создаётся View (Представление). Задачей View является ОТОБРАЖЕНИЕ данных. Ни какой обработки данных в View быть не должно! В отображение данных входит непосредственно показ самих данных, редактирование значений данных, WPF конвертация данных в удобный для отображения вид, команды для элементов (в первую очередь для кнопок и меню).
При создании View элементы надо к чему-то привязывать. Создавать абстрактные привязки, в которых на момент редактирования ничего нет, визуально трудно и приводит к многочисленным ошибкам. Поэтому перед созданием View надо создать ViewModel DesignData (Модель представления времени разработки). Она может быть как единая для всей View, так и раздробленная - отдельная для каждого элемента. И надо понимать VMDD - это часть View, а не VM. И заполняется данными она в XAML. Изменяя эти данные, очень удобно видеть как меняется View. Особенно в случаях когда в зависимости от данных должны скрываться/показываться разные элементы.
VM, в дальнейшем, удобно наследовать от VMDD. А так как VM должна поддерживать интерфейс INPC, класс VMDD будем наследовать от класса OnPropertyChangedClass. Для привязки команд в VMDD будем использовать класс RelayCommand.
Класс VMDD - FuzzySearch_VM_DataDesigner
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
    /// <summary>Класс ViewModel for DesignData</summary>
    public class FuzzySearch_VM_DataDesigner : OnPropertyChangedClass
    {
        #region Свойства для привязки элементов отображения
        /// <summary>Свойство для слова поиска</summary>
        public string TextSearch { get => _textSearch; set { _textSearch = value; OnAllPropertyChanged(); } }
        /// <summary>Свойство для слова добавления</summary>
        public string TextAdd { get => _textAdd; set { _textAdd = value; OnAllPropertyChanged(); } }
        /// <summary>Свойство для списка слова</summary>
        public ObservableCollection<string> ListWords { get => _listWords; set { _listWords = value; OnAllPropertyChanged(); } }
        /// <summary>Свойство для сообщения с результатами поиска</summary>
        public string TextMessage { get => _textMessage; set { _textMessage = value; OnAllPropertyChanged(); } }
        #endregion
 
        #region Приватные поля
        // Поля для хранения значения свойства
        private string _textSearch;
        private string _textAdd;
        private ObservableCollection<string> _listWords;
        private string _textMessage;
        // Поля для хранения значения команд
        private ICommand _searchComm;
        private ICommand _clearComm;
        private ICommand _addComm;
        private ICommand _saveComm;
        private ICommand _loadComm;
        #endregion
 
        #region Свойства для привязки команд
        /// <summary>Свойство для привязки команды</summary>
        public ICommand SearchComm => _searchComm ?? (_searchComm = new RelayCommand(OnSearch));
        /// <summary>Свойство для привязки команды</summary>
        public ICommand ClearComm => _clearComm ?? (_clearComm = new RelayCommand(OnClear));
        /// <summary>Свойство для привязки команды</summary>
        public ICommand AddComm => _addComm ?? (_addComm = new RelayCommand(OnAdd));
        /// <summary>Свойство для привязки команды</summary>
        public ICommand SaveComm => _saveComm ?? (_saveComm = new RelayCommand(OnSave));
        /// <summary>Свойство для привязки команды</summary>
        public ICommand LoadComm => _loadComm ?? (_loadComm = new RelayCommand(OnLoad));
        #endregion
 
        #region Методы для команд
        /// <summary>Метод для вызова из команды</summary>
        /// <param name="Value">Значение привязанного параметра</param>
        public virtual void OnSearch(object Value = null) { }
        /// <summary>Метод для вызова из команды</summary>
        /// <param name="Value">Значение привязанного параметра</param>
        public virtual void OnClear(object Value = null)
        { TextSearch = ""; TextMessage = ""; TextAdd = ""; }
        /// <summary>Метод для вызова из команды</summary>
        /// <param name="Value">Значение привязанного параметра</param>
        public virtual void OnAdd(object Value = null) { }
        /// <summary>Метод для вызова из команды</summary>
        /// <param name="Value">Значение привязанного параметра</param>
        public virtual void OnSave(object Value = null) { }
        /// <summary>Метод для вызова из команды</summary>
        /// <param name="Value">Значение привязанного параметра</param>
        public virtual void OnLoad(object Value = null) { }
        #endregion
    }
Заполняя данными VMDD в Xaml окна - используем префикс d:, который сообщает компилятору, что это данные времени разработки и компилировать в сборку их не надо. Для развитой VMDD для отладки View временно убирается префикс d: и исполняется проект. View должна быть полностью рабочей для такого исполнения! Допустим, в нашем случае, запуская окно мы можем изменять его размеры и видеть как адаптируются размеры элементов под размеры окна. Исходя из этого мы можем ограничить максимальные или минимальные размеры окна.
XML
1
2
3
4
5
6
7
8
9
    <d:Window.DataContext>
        <local:FuzzySearch_VM_DataDesigner TextSearch="Слово для поиска" TextAdd="Слово для добавления" TextMessage="Сообщение">
            <local:FuzzySearch_VM_DataDesigner.ListWords>
                <sys:String>Анекдоты</sys:String>
                <sys:String>про</sys:String>
                <sys:String>Вовочку</sys:String>
            </local:FuzzySearch_VM_DataDesigner.ListWords>
        </local:FuzzySearch_VM_DataDesigner>
    </d:Window.DataContext>
Теперь можно удобно создавать WPF View. Одна из частых ошибок для начинающих, особенно тех кто до этого работал в WinForms, "жёстко" позиционировать элементы с помощью свойств Margin и размера элементов. Для WPF такое НЕПРИЕМЛЕМО! Margin это свойство для задания расстояния между элементами, а не их позиции. Для компоновки элементов надо использовать различные типы контейнеров.
Для компоновки, в данном случае, хватает комбинации панелей Grid и StackPanel. Все элементы сразу привязываем к данным. Так как у нас есть заполненная VMDD, то в конструкторе всплывают свойства VMDD и в элементах сразу отображаются значения этих свойств.
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
    <Grid x:Name="Can1" ShowGridLines="False" Background="Aqua">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <StackPanel>
            <TextBox TextWrapping="Wrap" Text="{Binding TextSearch}" Margin="10"/>
            <Button Content="Поиск"  VerticalAlignment="Bottom" Margin="10" Command="{Binding SearchComm}"/>
        </StackPanel>
        <TextBlock Grid.Column="1" Margin="10" TextWrapping="Wrap" Background="White" Text="{Binding TextMessage}"/>
        <TextBox TextWrapping="Wrap" Text="{Binding TextAdd}" Margin="10" Grid.Row="2"/>
        <ListBox Margin="10" Grid.Row="1" MinHeight="20" Grid.ColumnSpan="3" ItemsSource="{Binding ListWords}"/>
        <Button  Content="Очистить"  VerticalAlignment="Bottom" Margin="10" Grid.Row="0" Grid.Column="2" Command="{Binding ClearComm}"/>
        <Button Content="Добавить"  VerticalAlignment="Center" Margin="10" Grid.Row="2" Grid.Column="1" Command="{Binding AddComm}"/>
        <StackPanel Grid.Row="2" Grid.Column="2">
            <Button Content="Загрузить" Margin="10" Command="{Binding LoadComm}"/>
            <Button Content="Сохранить" Margin="10" Command="{Binding SaveComm}"/>
        </StackPanel>
    </Grid>
После создания View надо создать реальную VM. В ней нужно прописать связь с Моделью. Так как у нас уже есть VMDD, то VM будем наследовать от неё. Нам понадобится прописать в конструкторе инициализацию Модели и свойства для списка слов и переопределить методы для кнопок.
Класс VM - FuzzySearch_VM
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    /// <summary>Класс реальной ViewModel</summary>
    public class FuzzySearch_VM : FuzzySearch_VM_DataDesigner
    {
        public FuzzySearch_VM()
        {
            FuzzySearch_Model.Load();
            ListWords = FuzzySearch_Model.ListWords;
        }
 
        public override void OnAdd(object Value = null) => FuzzySearch_Model.AddWord(TextAdd);
        public override void OnLoad(object Value = null) => FuzzySearch_Model.Load();
        public override void OnSave(object Value = null) => FuzzySearch_Model.Save();
        public override void OnSearch(object Value = null) => TextMessage = FuzzySearch_Model.SearchWord(TextSearch);
    }
Теперь осталось только подключить VM к View
XML
1
2
3
    <Window.DataContext>
        <local:FuzzySearch_VM/>
    </Window.DataContext>
На этом всё - наше WPF приложение в паттерне MVVM создано!
Архив проекта - приложен. В посте #24 дополненное решение (с учётом замечаний Storm23).
ОшибкаПеречитывая тему заметил, что в свойствах вызывается метод OnAllPropertyChanged();.
Это случайная ошибка, не заметил её.
Должен вызываться метод OnPropertyChanged();
Вложения
Тип файла: 7z FuzzySearch.7z (28.3 Кб, 258 просмотров)
Тип файла: zip FuzzySearch_v0.zip (78.3 Кб, 456 просмотров)
11
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
26.12.2018, 06:04
Ответы с готовыми решениями:

Куда вставлять какой код из темы "Пример реализации WPF+MVVM приложения"?
https://www.cyberforum.ru/wpf-silverlight/thread2380526.html Объясните, пожалуйста, куда вставлять какой код. (Не понятно как...

Прошу пример MVVM реализации
привет во всех примерах что изучал View ViewModel Code - datacontex=new ViewModel - и не видно привязки к обьекту Code прошу...

Пример переключения между окнами WPF MVVM
Здравствуйте. Изучаю как можно переходить между окнами в wpf в рамках MVVM. Изучаю данный пример: ...

45
-21 / 29 / 2
Регистрация: 17.03.2018
Сообщений: 778
27.12.2018, 21:30
Студворк — интернет-сервис помощи студентам
Почтальон,
Скрин

А как в отладчике смотреть где "точность" теряется?
Миниатюры
Пример реализации WPF+MVVM приложения  
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
27.12.2018, 21:32  [ТС]
Цитата Сообщение от Почтальон Посмотреть сообщение
Была проблема с TwoWay, пока на decimal не переписал
SOL - числа вроде в десятичном формате хранит? А к double просто приводит. Потеря точности при таком приведении - неизбежна.
Или я ошибаюсь?
0
-21 / 29 / 2
Регистрация: 17.03.2018
Сообщений: 778
27.12.2018, 21:35
Цитата Сообщение от Элд Хасп Посмотреть сообщение
SOL - числа вроде в десятичном формате хранит? А к double просто приводит. Потеря точности при таком приведении - неизбежна.
Или я ошибаюсь?
К счастью это не SQL. Точность там потрясающая. И в WinForm все ОК. Где-то в WPF MVVM теряется.

Оффтор: Проектик глянули?
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
06.01.2019, 05:37  [ТС]
С учётом замечаний Storm23 внёс изменения в проект. Несколько осложнился код Model и ViewModel, но это всё таки будет более правильным решением.
Модель также дополнил методом нечёткого поиска от alexus5 Подобрать из словаря слова, отличающиеся от введенного пользователем слова не более, чем на одну букву
Код Модели
Кликните здесь для просмотра всего текста
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
namespace FuzzySearch
{
    /// <summary>Статический класс модели данных</summary>
    public class FuzzySearch_Model
    {
 
        /// <summary>Имя файла по умолчанию</summary>
        public string NameFile { get; private set; } = "ListWords.txt";
        ObservableCollection<string> _listWords;
        /// <summary>Список слов</summary>
        public ObservableCollection<string> ListWords
        {
            get => _listWords ?? (_listWords = new ObservableCollection<string>());
            private set => _listWords = value;
        }
 
        /// <summary>Сохранение списка слов в файле</summary>
        public void Save()
        {
            if (string.IsNullOrWhiteSpace(NameFile))
                throw new ArgumentException("File name is empty", nameof(NameFile));
 
            using (StreamWriter file = new StreamWriter(NameFile, false, Encoding.Default))
                file.Write(string.Join(Environment.NewLine, ListWords));
        }
 
        /// <summary>Сохранение списка слов в файле  сзаданным именем</summary>
        /// <param name="NameFile">Имя файла</param>
        public void SaveAs(string NameFile)
        {
            if (string.IsNullOrWhiteSpace(NameFile))
                throw new ArgumentException("File name is empty", nameof(NameFile));
 
            using (StreamWriter file = new StreamWriter(NameFile, false, Encoding.Default))
                file.Write(string.Join(Environment.NewLine, ListWords));
 
            this.NameFile = NameFile;
        }
 
        /// <summary>Чтение списка слов из файла</summary>
        /// <param name="NameFile">Имя файла</param>
        public void Load(string NameFile = null)
        {
            if (string.IsNullOrWhiteSpace(NameFile))
                NameFile = this.NameFile;
 
            if (!File.Exists(NameFile))
                throw new ArgumentException("file does not exist", NameFile);
 
            using (StreamReader file = new StreamReader(NameFile, Encoding.Default))
                ListWords = new ObservableCollection<string>(file.ReadToEnd().Split("\r\n ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Select(wrd => wrd.ToLower()));
 
        }
 
        /// <summary>Добавление слова в список</summary>
        /// <param name="Words">Слово или список слов разделённых переносом строки или пробелом</param>
        public void AddWord(string Words)
        {
            foreach (string word in Words.ToLower().Split("\r\n ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
                ListWords.Add(word);
        }
 
        /// <summary>Поиск слова в списке</summary>
        /// <param name="Word">Одно слово</param>
        /// <returns>Результат поиска</returns>
        public SearchResult SearchWord(string Word)
        {
            string[] wordArr = Word.ToLower().Split("\r\n ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            if (wordArr.Length != 1)
                throw new ArgumentException("You need to pass single, non empty word", nameof(Word));
 
            Word = wordArr[0];
            int index = ListWords.IndexOf(Word);
            if (index >= 0)
                return new SearchResult { Type = ResultType.ExactMatch, Index = index };
            /// *************************
            /// Нечёткий поиск
            /// *************************
            for (index = 0; index < ListWords.Count; index++)
                if (IsNotMoreOneDifferense(Word, ListWords[index]))
                    return new SearchResult { Type = ResultType.PartialMatch, Index = index };
            return new SearchResult { Type = ResultType.NoMatch, Index = -1 };
        }
 
        //   https://www.cyberforum.ru/csharp-beginners/thread2376045.html#post13174564
        /// <summary>Метод нечёткого сравнения двух строк</summary>
        static bool IsNotMoreOneDifferense(string key, string str)
        {
            int error = 0;
            int delta = key.Length - str.Length;
            if (Math.Abs(delta) > 1) return false;
            int count = Math.Min(key.Length, str.Length);
            // начинаем посимвольное сравнение
            for (int i = 0, j = 0; i < count; i++, j++)
            {
                if (key[i] != str[j])
                {
                    error++;
                    // предел ошибок
                    if (error > 1) return false;
                    // оставляем неподвижным ползунок у кого слова, которое короче
                    // если образец
                    if (delta > 0) j--;
                    // если проверочное слово
                    if (delta < 0) i--;
                    // если длины слов одинаковы, ничего не менять
                }
            }
            return true;
        }
 
        /// <summary>Класс для результатов поиска</summary>
        public class SearchResult
        {
            public ResultType Type;
            public int Index;
        }
 
        /// <summary>Перечисление результатов поиска</summary>
        public enum ResultType
        {
            ExactMatch,
            PartialMatch,
            NoMatch
        }
    }
}

В View (и соответственно в VMDD и VM) добавил кнопку "Сохранить в..."
Код VMDD
Кликните здесь для просмотра всего текста
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
namespace FuzzySearch
{
    /// <summary>Класс ViewModel for DesignData</summary>
    public class FuzzySearch_VM_DataDesigner : OnPropertyChangedClass
    {
        #region Свойства для привязки элементов отображения
        /// <summary>Свойство для слова поиска</summary>
        public string TextSearch { get => _textSearch; set { _textSearch = value; OnAllPropertyChanged(); } }
        /// <summary>Свойство для слова добавления</summary>
        public string TextAdd { get => _textAdd; set { _textAdd = value; OnAllPropertyChanged(); } }
        /// <summary>Свойство для списка слова</summary>
        public ObservableCollection<string> ListWords { get => _listWords; set { _listWords = value; OnAllPropertyChanged(); } }
        /// <summary>Свойство для сообщения с результатами поиска</summary>
        public string TextMessage { get => _textMessage; set { _textMessage = value; OnAllPropertyChanged(); } }
        #endregion
 
        #region Приватные поля
        // Поля для хранения значения свойства
        private string _textSearch;
        private string _textAdd;
        private ObservableCollection<string> _listWords;
        private string _textMessage;
        // Поля для хранения значения команд
        private ICommand _searchComm;
        private ICommand _clearComm;
        private ICommand _addComm;
        private ICommand _saveComm;
        private ICommand _saveAsComm;
        private ICommand _loadComm;
        #endregion
 
        #region Свойства для привязки команд
        /// <summary>Свойство для привязки команды</summary>
        public ICommand SearchComm => _searchComm ?? (_searchComm = new RelayCommand(OnSearch));
        /// <summary>Свойство для привязки команды</summary>
        public ICommand ClearComm => _clearComm ?? (_clearComm = new RelayCommand(OnClear));
        /// <summary>Свойство для привязки команды</summary>
        public ICommand AddComm => _addComm ?? (_addComm = new RelayCommand(OnAdd));
        /// <summary>Свойство для привязки команды</summary>
        public ICommand SaveComm => _saveComm ?? (_saveComm = new RelayCommand(OnSave));
        /// <summary>Свойство для привязки команды</summary>
        public ICommand SaveAsComm => _saveAsComm ?? (_saveAsComm = new RelayCommand(OnSaveAs));
        /// <summary>Свойство для привязки команды</summary>
        public ICommand LoadComm => _loadComm ?? (_loadComm = new RelayCommand(OnLoad));
        #endregion
 
        #region Методы для команд
        /// <summary>Метод для вызова из команды</summary>
        /// <param name="Value">Значение привязанного параметра</param>
        public virtual void OnSearch(object Value = null) { }
        /// <summary>Метод для вызова из команды</summary>
        /// <param name="Value">Значение привязанного параметра</param>
        public virtual void OnClear(object Value = null)
        { TextSearch = ""; TextMessage = ""; TextAdd = ""; }
        /// <summary>Метод для вызова из команды</summary>
        /// <param name="Value">Значение привязанного параметра</param>
        public virtual void OnAdd(object Value = null) { }
        /// <summary>Метод для вызова из команды</summary>
        /// <param name="Value">Значение привязанного параметра</param>
        public virtual void OnSave(object Value = null) { }
        /// <summary>Метод для вызова из команды</summary>
        /// <param name="Value">Значение привязанного параметра</param>
        public virtual void OnSaveAs(object Value = null) { }
        /// <summary>Метод для вызова из команды</summary>
        /// <param name="Value">Значение привязанного параметра</param>
        public virtual void OnLoad(object Value = null) { }
        #endregion
    }
}
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
<Window x:Class="FuzzySearch.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:FuzzySearch"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        Title="MainWindow" MinHeight="300" MinWidth="300">
    <Window.DataContext>
        <local:FuzzySearch_VM/>
    </Window.DataContext>
    <d:Window.DataContext>
        <local:FuzzySearch_VM_DataDesigner TextSearch="Слово для поиска" TextAdd="Слово для добавления" TextMessage="Сообщение">
            <local:FuzzySearch_VM_DataDesigner.ListWords>
                <sys:String>Анекдоты</sys:String>
                <sys:String>про</sys:String>
                <sys:String>Вовочку</sys:String>
            </local:FuzzySearch_VM_DataDesigner.ListWords>
        </local:FuzzySearch_VM_DataDesigner>
    </d:Window.DataContext>
    <Grid x:Name="Can1" ShowGridLines="False" Background="Aqua">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <StackPanel>
            <TextBox TextWrapping="Wrap" Text="{Binding TextSearch}" Margin="10"/>
            <Button Content="Поиск"  VerticalAlignment="Bottom" Margin="10" Command="{Binding SearchComm}"/>
        </StackPanel>
        <TextBlock Grid.Column="1" Margin="10" TextWrapping="Wrap" Background="White" Text="{Binding TextMessage}"/>
        <TextBox TextWrapping="Wrap" Text="{Binding TextAdd}" Margin="10" Grid.Row="2"/>
        <ListBox Margin="10" Grid.Row="1" MinHeight="20" Grid.ColumnSpan="3" ItemsSource="{Binding ListWords}"/>
        <Button  Content="Очистить"  VerticalAlignment="Center" Margin="10" Grid.Row="0" Grid.Column="2" Command="{Binding ClearComm}"/>
        <StackPanel Grid.Row="2" Grid.Column="1">
            <Button Content="Добавить"  Margin="10" Command="{Binding AddComm}"/>
            <Button Content="Загрузить" Margin="10" Command="{Binding LoadComm}"/>
        </StackPanel>
        <StackPanel Grid.Row="2" Grid.Column="2">
            <Button Content="Сохранить" Margin="10" Command="{Binding SaveComm}"/>
            <Button Content="Сохранить в ...." Margin="10" Command="{Binding SaveAsComm}"/>
        </StackPanel>
    </Grid>
</Window>
Код 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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
namespace FuzzySearch
{
    /// <summary>Клас реальной ViewModel</summary>
    public class FuzzySearch_VM : FuzzySearch_VM_DataDesigner
    {
        FuzzySearch_Model model;
        public FuzzySearch_VM()
        {
            model = new FuzzySearch_Model();
            ListWords = model.ListWords;
        }
 
        public override void OnAdd(object Value = null) => model.AddWord(TextAdd);
        public override void OnLoad(object Value = null)
        {
            // https://docs.microsoft.com/ru-ru/dotnet/api/microsoft.win32.openfiledialog?view=netframework-4.7.2
            // Configure open file dialog box
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.FileName = model.NameFile; // Default file name
            dlg.DefaultExt = ".txt"; // Default file extension
            dlg.Filter = "Text documents (.txt)|*.txt"; // Filter files by extension
 
            // Show open file dialog box
            bool? result = dlg.ShowDialog();
 
            // Process open file dialog box results
            if (result != true)
                return;
 
                // Open document
            try
            {
                model.Load(dlg.FileName);
                ListWords = model.ListWords;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        public override void OnSave(object Value = null)
        {
            try
            {
                model.Save();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        public override void OnSaveAs(object Value = null)
        {
            // https://docs.microsoft.com/ru-ru/dotnet/api/microsoft.win32.savefiledialog?view=netframework-4.7.2
            // Configure save file dialog box
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.FileName = model.NameFile; // Default file name
            dlg.DefaultExt = ".txt"; // Default file extension
            dlg.Filter = "Text documents (.txt)|*.txt"; // Filter files by extension
 
            // Show save file dialog box
            bool? result = dlg.ShowDialog();
 
            // Process save file dialog box results
            if (result != true)
                return;
 
                // Save document
            try
            {
                model.SaveAs(dlg.FileName);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
 
        public override void OnSearch(object Value = null)
        {
 
            try
            {
                SearchResult res = model.SearchWord(TextSearch);
                switch (res.Type)
                {
                    case ResultType.ExactMatch: TextMessage = "Tочное совпадение со словом по индексу " + res.Index; break;
                    case ResultType.PartialMatch: TextMessage = "Частичное совпадение со словом по индексу " + res.Index; break;
                    default: TextMessage = "Нет совпадений со словами из списка"; break;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }
}


Полный архив обоих проектов приложен

ОшибкаПеречитывая тему заметил, что в свойствах вызывается метод OnAllPropertyChanged();.
Это случайная ошибка, не заметил её.
Должен вызываться метод OnPropertyChanged();
Вложения
Тип файла: zip FuzzySearch (Two variant).zip (163.3 Кб, 186 просмотров)
0
HF
 Аватар для HF
1340 / 924 / 202
Регистрация: 09.09.2011
Сообщений: 2,743
Записей в блоге: 2
06.01.2019, 16:46
Цитата Сообщение от Элд Хасп Посмотреть сообщение
На этом всё - наше WPF приложение в паттерне MVVM создано!
Архив проекта - приложен. В посте #24 дополненное решение (с учётом замечаний Storm23).
1) текущий аттач не качается. Может быть тогда сразу заменить его на тот который из ссылки? Или удалить и ссылку оставить.

2) "Main_VM" ? Обычно придерживаются стиля "MainViewModel". Так как и My1Property, а не My1_Property и т.п. Не видел уже лет 100 чтобы "_" использовали в названиях.

3) между предыдущим кодом и комментарием следующего кода (свойства) лучше перенос использовать. Иначе нечитабельно, особенно когда кода очень много.

Добавлено через 1 час 34 минуты
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Поэтому перед созданием View надо создать ViewModel DesignData (Модель представления времени разработки)
...
Заполняя данными VMDD в Xaml окна - используем префикс d:, который сообщает компилятору, что это данные времени разработки и компилировать в сборку их не надо. Для развитой VMDD для отладки View временно убирается префикс d: и исполняется проект. View должна быть полностью рабочей для такого исполнения! Допустим, в нашем случае, запуская окно мы можем изменять его размеры и видеть как адаптируются размеры элементов под размеры окна. Исходя из этого мы можем ограничить максимальные или минимальные размеры окна.
Первый раз услышал про VMDD. Пришлось почитать (спасибо за новые знания ).
Нигде не вижу примеров где бы это советовали и использовали. Повидимому это какой-то специфичный подход. Даже не представляю пока где бы это использовать. Что такое "на стадии разработки"? их потом нужно убрать?

В этом блоке (про VMDD, ":d" префикс, notifyproperty и пример кода) помоему собрано много тем сразу, которые желательно описать раздельно. А то складывается ощущение что это единственно верный подход.

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Теперь осталось только подключить VM к View
В этом блоке можно дописать, что здесь подключается ViewModelLocator, если он реализован. Вдруг потом такую тему тоже напишите.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
06.01.2019, 17:19  [ТС]
Цитата Сообщение от HF Посмотреть сообщение
1) текущий аттач не качается. Может быть тогда сразу заменить его на тот который из ссылки? Или удалить и ссылку оставить.
Поменял на ZIP. Предыдущий 7Z почему-то у кого-то скачивается, у кого-то нет.
Цитата Сообщение от HF Посмотреть сообщение
2) "Main_VM" ? Обычно придерживаются стиля "MainViewModel". Так как и My1Property, а не My1_Property и т.п. Не видел уже лет 100 чтобы "_" использовали в названиях.
3) между предыдущим кодом и комментарием следующего кода (свойства) лучше перенос использовать. Иначе нечитабельно, особенно когда кода очень много.
Учту, на будущее. Мне казалось так понятнее, но Вы не единственный кто написал, что это неверно.
Цитата Сообщение от HF Посмотреть сообщение
Первый раз услышал про VMDD. Пришлось почитать (спасибо за новые знания ).
Нигде не вижу примеров где бы это советовали и использовали. По видимому это какой-то специфичный подход. Даже не представляю пока где бы это использовать. Что такое "на стадии разработки"? их потом нужно убрать?
Даже конструктор привязок предлагает создать ".... Можно задать DataContext времени разработки, выбрав ...."

А как Вы заполняете значениями элементы во время разработки (в редакторе WPF)? Или они у Вас пустые?

Всё что с префиксом d: используется только в редакторе. Это в код не попадает. Я обычно заполняю VMDD в словаре XAML и потом подключаю по ключу. Редактирование становится намного удобнее. Ведь на этапе создания View ещё не создана ViewModel. А с VMDD есть к чему делать привязки, WPF конструктор видит нужные свойства. Отлаживать View тоже можно без VuewModel. Очень удобно.
Цитата Сообщение от HF Посмотреть сообщение
В этом блоке можно дописать, что здесь подключается ViewModelLocator, если он реализован. Вдруг потом такую тему тоже напишите.
В этом отношении не знаю. Сам пока начинающий. Поэтому думаю только о том как довести до ещё более начинающих саму концепцию WPF и MVVM.
Может когда созрею до среднечка, то продолжу эти темы. А пока не тяну. Мне бы самому кто-нибудь помог, в то числе с Prism, MVVMLight.
0
HF
 Аватар для HF
1340 / 924 / 202
Регистрация: 09.09.2011
Сообщений: 2,743
Записей в блоге: 2
06.01.2019, 17:41
Цитата Сообщение от Элд Хасп Посмотреть сообщение
А как Вы заполняете значениями элементы во время разработки (в редакторе WPF)? Или они у Вас пустые?
Раньше делал по самому простому варианту - единственная строка в Code-Behind, в конструкторе в виде
C#
1
DataContext = new MainWindowViewModel();
Вариант - не новый объект, а или объект из конструктора или свойство которое доступно для изменения.
А сейчас вот периодически использую или через Locator или как в этом коде. Самое простое подключение.
или через "шапку" или код-блок. "Шапка" короче получается.
C#
1
2
<Window ...
    DataContext="{Binding Source={StaticResource local:FuzzySearch_VM}}"
C#
1
2
3
     <Window.DataContext>
         <local:FuzzySearch_VM/>
     </Window.DataContext>
и
А тот код о котором я говорил - ваш VMDD пример. Он напоминает сотворение вьюмодели прямо в разметке. Тут и модель, тут и свойства, тут и какое-то наполнение. Вот мне и не понятно стало - это использование на момент разработки или вообще где-то так стали практиковать?

Добавлено через 9 минут
Про "шапку" ошибонька. Там конечно же не так - нужно в классе иметь поле, а потом в "шапке" её биндить.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
06.01.2019, 17:44  [ТС]
Цитата Сообщение от HF Посмотреть сообщение
Раньше делал по самому простому варианту - единственная строка в Code-Behind, в конструкторе в виде
Но для этой строки уже нужна созданная ViewModel. Но на момент создания View её же ещё нет. И как WPF редактор увидит, что подключено в коде DataContext?
Цитата Сообщение от HF Посмотреть сообщение
А тот код о котором я говорил - ваш VMDD пример. Он напоминает сотворение вьюмодели прямо в разметке. Тут и модель, тут и свойства, тут и какое-то наполнение. Вот мне и не понятно стало - это использование на момент разработки или вообще где-то так стали практиковать?
Я не знаю на счёт "практиковать". Я только когда начал разбираться с WPF увидел эту подсказку в редакторе (о создании DataContext времени разработки). Начал разбираться что это такое. И эти разборки выросли в такую концепцию.
Честно говоря, Вы меня озадачили.... Я думал это типичный подход... Тем более что во многом мне помогли и направили "мастера этого дела".
Что касается подключения реальной VM не в коде, а в XAML. Во многих случаях это возможно (тем более в простых у начинающих) и удобно. Прививает навыки работы с XAML, отсутствие которых частая проблема начинающих на WPF.
Можно биндить как Вы показали. Но принципиальная разница какая?
0
Заблокирован
06.01.2019, 19:44
Цитата Сообщение от Почтальон Посмотреть сообщение
очень рекомендую эту книгу
Какого года последнее издание было? 2010?
0
HF
 Аватар для HF
1340 / 924 / 202
Регистрация: 09.09.2011
Сообщений: 2,743
Записей в блоге: 2
06.01.2019, 20:03
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Честно говоря, Вы меня озадачили.... Я думал это типичный подход... Тем более что во многом мне помогли и направили "мастера этого дела".
Не буду спорить и переубеждать. На всякий случай сам засомневался в своей правоте. Но на всякий случай открыл Метанит - там просто в классе определено. Я то учился по иностранным обучающим видео. Нигде такого подхода не видел.
А вообще почему я на это внимание обратил? - я всегда видел строчку mc:Ignorable="d", и никогда не видел чтобы она использовалась. И тут бац! в ваших текстах, и так активно используется. Конечно это меня заинтересовало.
0
-21 / 29 / 2
Регистрация: 17.03.2018
Сообщений: 778
06.01.2019, 20:13
Цитата Сообщение от titan4ik Посмотреть сообщение
Какого года последнее издание было? 2010?
2017.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
06.01.2019, 20:46  [ТС]
Цитата Сообщение от HF Посмотреть сообщение
А вообще почему я на это внимание обратил? - я всегда видел строчку mc:Ignorable="d", и никогда не видел чтобы она использовалась. И тут бац! в ваших текстах, и так активно используется. Конечно это меня заинтересовало.
Попробуйте - очень удобно.
0
-21 / 29 / 2
Регистрация: 17.03.2018
Сообщений: 778
06.01.2019, 20:53
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Попробуйте - очень удобно.
а для чего это вообще?
XML
1
mc:Ignorable="d"
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
06.01.2019, 22:24  [ТС]
Цитата Сообщение от Bulky Посмотреть сообщение
а для чего это вообще?
Указание, что компилировать в код эти значения не надо.

Добавлено через 5 минут
Можно установить два источника данных.
Один на время дизайна для работы редактора, конструктора.
Другой для реального исполнения.
0
196 / 41 / 24
Регистрация: 24.02.2013
Сообщений: 231
27.02.2019, 12:50
Можно узнать что происходит тут?

C#
1
2
3
4
5
6
7
public void OnPropertyChanged([CallerMemberName]string prop = "")
        {
            string[] names = prop.Split("\\/\r \n()\"\'-".ToArray(), StringSplitOptions.RemoveEmptyEntries);
            if (names.Length != 0)
                foreach (string _prp in names)
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(_prp));
        }
Ведь если сделать так, то все равно работает


C#
1
2
3
4
5
public void OnPropertyChanged([CallerMemberName]string prop = "")
        {
            
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
        }
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
27.02.2019, 15:41  [ТС]
Цитата Сообщение от alexSo94 Посмотреть сообщение
Можно узнать что происходит тут?
Иногда возникает необходимость послать сигнал обновления одного свойства из сеттера другого.

Пример:

C#
1
2
public bool IsZero =>  Value == 0;
public int Value { get => _value; set {_value = value; OnPropertyChanged(); OnPropertyChanged("IsZero");}}
Можно вызвать так

C#
1
2
public bool IsZero =>  Value == 0;
public int Value { get => _value; set {_value = value; OnPropertyChanged("Value IsZero");}}
1
196 / 41 / 24
Регистрация: 24.02.2013
Сообщений: 231
27.02.2019, 15:53
А вот эта строка? получаем массив названий имен свойств ?
C#
1
string[] names = prop.Split("\\/\r \n()\"\'-".ToArray(), StringSplitOptions.RemoveEmptyEntries);
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
27.02.2019, 16:03  [ТС]
Цитата Сообщение от alexSo94 Посмотреть сообщение
А вот эта строка? получаем массив названий имен свойств ?
Да. И потом вызываем PropertyChanged для каждого свойства.
0
В поиске
104 / 52 / 17
Регистрация: 20.04.2014
Сообщений: 826
06.05.2019, 19:20
Люди добрые объясните очень далекому человеку для чего в этом примере FuzzySearch_VM_DataDesigner и FuzzySearch_VM все перечитал ни могу вразумить не как возникает ощущение что FuzzySearch_VM_DataDesigner это как интерцейс но зачем? делать какую то слабую связь?
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
06.05.2019, 20:47  [ТС]
Цитата Сообщение от КВА696 Посмотреть сообщение
FuzzySearch_VM_DataDesigner это как интерцейс
Для того чтобы были данные для редактора-конструктора Студии.

Если делать по максимуму (в смысле подробностей), то, конечно, нужно сначала сделать интерфейсы от Модели к VM и от VM к View.

Но в View нельзя заполнить интерфейс экземплярами. Поэтому для отладки View (проверить как она смотрится с теми или иными значениями, как скрываются элементы, меняются цвета, адаптируются размеры и т.д) надо создать какую-то реализацию этого интерфейса, чтобы можно было заполнить её значениями и какой-то логикой в пределах View. И уже на этом отладить View.

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

В данном случае так и сделано. Создана FuzzySearch_VM_DataDesigner. В ней полностью реализован интерфейс для подключения View. Она использована для отладки View. Изменяя значения её свойств в XAML можно видеть как на это реагирует View. То есть это фактически часть View.

После того как добились нужного результата, создали производный от него класс FuzzySearch_VM и дописали часть работающую с Моделью.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
06.05.2019, 20:47

Как разбить WPF, MVVM по проектах? Пример команды
добрый день, я делаю большой проект на WPF с использованием паттерна MVVM. как мне его правильно разбить по проектам в VisualStudio? я так...

Ищу пример wpf MVVM добавления записей в таблицу DataGrid
Дайте пожалуйста ссылку на пример где объясняется как добавлять записи через DataGrid, чтобы при добавлении новой записи коллекция...

Структура WPF приложения на MVVM
Здравствуйте. Разрабатываю приложение на WPF. Использую шаблон MVVM. В представлении контент сгруппирован по вкладкам TabControl. ...

Паттерн MVVM или как писать приложения на WPF
Собтвенно вопрос в заголовке. По-скольку WPF поддерживает привязку различным образом наверно это нужно как-то использовать, а не просто как...

Создание приложения "Штатное Расписание" в паттерне MVVM [WPF, Элд Хасп]
Тема из цикла https://www.cyberforum.ru/wpf-silverlight/thread2384523.html Тема создана как продолжение темы...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru