154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
|
||||||
1 | ||||||
WPF Установка Control.Style не триггерит set функционал у свойства класса21.05.2019, 12:59. Показов 3278. Ответов 33
Всем привет.
У меня есть класс - CoordinateView (клеточка). Он экстендит класс Grid. Также в нём есть два поля - TextBlock и Rectangle. Они добавлены в этот класс как его Children. Первый преднозначен для того чтобы показывать текст, другой нужен чтобы устанавливать цвет и толщину рамок . То есть, задача этой структуры - иметь рамки, цвет и текст. (Если есть такой control по дефолту, дайте знать, я не нашёл, и сделал вот Grid с TextBlock и Rectangle в качестве его "детей", но вопрос пока не в этом). Далее, я хочу установить свойства этой моей клеточке. Всякие разные, один из которых, например, Margin. Но хочу это делать динамично. Поэтому использую свойство Style и создаю объект из вне и добавляю в него Setter'ы того, что мне нужно. И всё работает. Всё, чётенько и классненько. Почти. В основном свойства, которые я устанавливаю относятся именно к классу Grid. Но вот есть несколько значений свойств, которые мне нужно передать "детям" (TextBlock и Rectangle), например Label и StrokeThickness. Эти два поля у меня приватные, я не хочу из вне чтобы был доступ к ним. Поэтому я создал wrapping свойства в классе своей клеточки в которых устанавливаю значения "детям". Получается вот так:
Вопрос: как сделать так, чтобы rectangle.Stroke всё-таки получал это значение, когда я устанавливаю стиль моей клеточки. Заранее спасибо!
0
|
21.05.2019, 12:59 | |
Ответы с готовыми решениями:
33
Работа свойства Bottom класса Control Выполнить команду в set свойства для свойства SelectedItem (ComboBox ) MVVM Как дополнить функционал класса из другого класса Свойства get и set |
Модератор
|
|
29.05.2019, 10:04 | 21 |
Хотя MVVM, на сколько помню, "вырос" из MVC между ними есть разница.
В MVC всем управляет контроллер. И у него "есть право лезть" как в View, так и в Model. Взаимодействие построено по схеме: "Запросил данные от View" -> "Отправил данные в Model" -> "Получил ответ от Model" -> "Записал данные в View". Так как здесь инициация всех действий лежит на контроллере, то это удобный способ реализации WF приложений. Для них это основной паттерн. В MVVM взаимодействие построено по другой схеме. Там несколько независимы ветвей:
Так как действия в MVVM инициируются уже не контроллером, а в View, то в для связи View с VM нужен соответствующий инструмент. в WF его нет и приходится его реализовывать кастомно. Это достаточно сложно. Поэтому приложения MVVM для WF - редкость. В WPF специально для этого был создан механизм привязок. Привязки View вызывают методы VM. Именно по этому привязки возможны только к свойствам - им нужны методы set и get .Какие данные View должна отправлять в VM? Это те данные которые после обработки должны быть отправлены в Model. Те данные которые необходимы для взаимодействия внутри View должны обрабатываться в ней самой. Очень часто для этого используются WPF конвертеры. Что для игры "Морской бой" должна делать Model? Представьте себе как эта Model будет работать с консолью. Модель создаёт Море и Корабли в этом Море. Какое внешние данные нужны Модели (после создания карты)? Только координаты очередного выстрела. Поэтому у Модели будет только один метод для получения данных. После получения выстрела ни View, ни VM не могут знать попал этот выстрел или нет. Полученный выстрел обрабатывается Моделью и если было попадание, то Модель изменяет свои данные - взрывает палубу Корабля и извещает об этом VM. И только обработав это извещение VM и View узнают что было попадание. View заданным образом изменяет отображение подбитой палубы. Вот теперь сами ответьте - Какое отношение стиль клетки имеет к данным Модели? Если ни какого, то зачем этот стиль нужен VM? Он абсолютно там не нужен. В общих чертах это должно быть так. В VM есть список клеток Моря. У этих клеток есть определённые возможные состояния (обычно перечисление). View выводит этот список как квадрат с заданными размерами. И каждый квадратик отображается в зависимости от своего состояния заданным в View образом. Сама же View (при клике по клетке, или через поле с координатами) отправляет в VM только координаты очередного выстрела. Вот зачем здесь передавать в VM стиль? Просто чтобы "жизнь малиной не казалась"? Стиль нужен только для настройки отображения клетки в зависимости от её состояния и никуда дальше View стиль "носа показывать" не должен. Посмотрите как в посте Суммирование элементов в 2048 я вывожу поле 4х4 и настроено отображение клеток. Как передаются данные между частями MVVM. Нужен ли для каждой View свой индивидуальный VM? По разному. Если нет других вариантов, то приходится делать разные. Но это вынужденная и редкая мера. В основном VM делается так, что она не знает какая View к ней подключена. Может WPF, а может консоль. VM - это должно быть "по барабану". Это не литература. Это темы с форума где я старался объяснить WPF и MVVM.
0
|
154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
|
|
29.05.2019, 11:27 [ТС] | 22 |
Как-то Вы превозносите MVVM над MVC, на мой взгляд. Про MVC говорите небрежно ("есть право лезть"). А про MVVM раза три упомянули, что только "по необходимости" общается. Так-то в MVC тоже компоненты общаются по необходимости. Но это всё неважно.
А важно вот что: View никаких данных не отправляет. Я ни разу не сказал, что view (клеточка) должна отправлять данные классу viewModel. Мой класс viewModel имеет поле координаты и еще он привязан к моему view (клеточке). Когда на клеточку нажимают, срабатывает команда и выполняется функция в моей viewModel. Там, в этой функции, мы используем модель и проверяем есть ли корабль на этой координате; если есть, то проверяем убит ли он (помимо того, что он ранен). Я думал, о конвертерах, но решил их не использовать по той причине, что в данном случае будет не просто конвертация, а... Так, меня по ходу осенило, пока я писал это. Ведь мы действительно конвертируем мимо/ранил/убил в цвет... Может быть и действительно стоит использовать конвертер. Но всё равно я должен в своём viewModel описать логику получаения результата (мимо/ранил/убил). Так? Я ведь не могу привязать свой view (клеточку) к модели корабля. Добавлено через 2 минуты Как это сделать? Модель должна быть привязяна к view? Я читал в MSDN, что обычно привязывают ViewModel с V, и реже Model с View. Если я привяжу view к модели, то как у меня будет работать viewModel? Я ведь не могу привязать мой view сразу и к viewModel и к модели. Добавлено через 2 минуты Откуда (view или viewModel) должен вызываться метод выстрела? Я думаю, что из viewModel. Добавлено через 2 минуты Так мой стиль не имеет отношения к модели. Я его добавил в viewModel как Dependency Injection, чтобы потом возвращать, когда нужно. Добавлено через 3 минуты Откуда у view данные о координате? Мой view имеет только стиль. Но у меня есть модель координаты, и класс battlefield (тоже модель) который хранит в себе этот список координат. Именно модель, а не view или viewModel, потому что AI будет стрелять в игрока используя модель, а не клеточки на экране. Добавлено через 6 минут Я в VM из view не передаю стиль. Я его передал один раз в конструктере при создании своего viewModel, чтобы он этот стиль возвращал клеточке. И ещё, Вы пишете вот это Означает ли это, что нужен только один экземпляр класса viewModel для каждого экземпляра моей клеточки (view)? Мне казалось, что кол-во экземпляров viewModel должно соответствовать кол-ву экземпляров клеточек, иначе как привязка будет работать... Но если я прав, то зачем в VM иметь целый список клеток Моря? То есть у мены будет сто экземпляров viewModel, в каждом из которых будет по сто клеток? Кстати, о каких клетках идёт речь, о виде или о моделе? Добавлено через 5 минут Или вы имеете ввиду поднять эвент в модели и подписаться к нему из viewModel?
0
|
Модератор
|
|
29.05.2019, 13:02 | 23 |
При чём здесь
небрежность ? Просто Контролер может напрямую сам, по собственной инициативе обращаться как к View, так и к Model.В MVVM по другому - Model не может обращаться ни к кому, VM может обращаться только к Model, View - только к VM. Все UI элементы и свойства должны быть сосредоточены в View и не покидать её. Вы что-то не так поняли. Привязка - это возможность обратиться к чьим-то методам. Так вот VM не может обращаться к методам View, то есть выражаясь по иному VM не может быть привязана к View. View привязана к VM, а VM к Model. В MVC контроллер может быть привязан как к View, так к Model. Model это без разницы. А в плане MVVM - все действия инициируются пользователем, то есть они в приложение поступают через View. View в ответ на действие пользователя может вызвать метод VM, а та может вызвать метод Model. Здесь у нас опять какое-то недопонимание друг друга. "Морской бой" - это игра для двоих. У каждого игрока есть два "Моря": одно с расстановкой своих кораблей, второе - для обстрела врага. Что это за игроки: пользователи или AI - неважно. Модель описывает одно Море. VM создаёт две модели и в зеркальном виде предоставляет доступ к ним для двух игроков. Своё Море игрок видит полностью, в море противника видит только подбитые палубы. Выстрелы от одного игрока VM передаёт в Модель противника. AI - это тоже такой же игрок. И взаимодействует с Моделью так же через VM. А зачем? VM - передаёт View состояние клеточки. А как это состояние отобразить это дело уже View. Зачем VM знать о том как View отображает клеточку? Это, совершенно, излишнее знание. Если это локальная игра против AI, то модель может быть одна. Она будет предоставлять данные обоим игрокам. Если это сетевая игра, то у каждого игрока будет свой экземпляр View на локальном компе и для каждого их них будет нужен свой экземпляр VM. При изменении данных объект уведомляет об этом своих подписчиков через событие. Это стандартное в Net взаимодействие.
0
|
Модератор
|
|
29.05.2019, 13:11 | 24 |
Нет.
Чаще всего для простых приложений: Несколько View -> Одна VM -> Одна Model. Для "Морского боя": Если локально с AI: Две View: одна окно игрока, вторая AI без окна -> Одна VM -> Две Model. Но можно и сделать для каждого игрока по своему экземпляру VM, что бы решение было однотипным для Локальной и Сетевой игры. Тогда будет такая схема: View первого игрока или AI -> локальная VM -> Model на сервере <- локальная VM <- View второго игрока или AI
1
|
154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
|
|
29.05.2019, 14:09 [ТС] | 25 |
Спасибо за ответ, Элд Хасп,
Напишу как я Вас понял, и хотел бы чтобы вы подтвердили или опровергли достоверность моего понимания. 1). View (клеточка), после того как игрок нажал на нее, тригеррит функцию в VM (через ICommand). 2). Та, в свою очередь, обращается к модели (а может и к нескольким моделям), дабы узнать статус выстрела. 3). После того, как VM получила ответ от модели, она устанавливает это значение своему свойству, на которое есть binding у view (клеточки). 4). На клеточке срабатывает конвертер и он в зависимости от этого результата (типа Enum, как Вы предложили) выбирает себе нужный стиль и устанавливает его. Если это так, то у меня всё еще есть два вопроса. Вы отвечали на один из них, но я всё равно не совсем понял, извините. 1). Когда происходит клик по клетке игроком, то, как я уже сказал выше, тригеррится соответствуюшая функция в VM. Поскольку мой вид это FrameworkElement, он не имеет координаты в себе. Он не знает, что он есть A5 или C4. Как в функции ViewModel, что срабатывает, когда кликают по клетке найти соответствующую этой клетке координату (координата у нас это модель!)? Я изначально в VM в конструктере как аргумент эту координату передаю чтобы VM потом её использовал как раз в случае клика по клетке. Но у меня такое ощущение, что Вы не имеете ввиду, что каждой клетке должен соответствовать новый экземпляр VM. Поэтому второй вопрос: 2). Как должен выглядеть VM в этом случае? Объясните пожалуйста подробно, какие свойства, поля и методы должны быть у VM. Сколько экземпляров надо создавать? И какие свойства bind'ить с клеточками или с чем там их лучше bind'ить? Простите меня за моё тугадумство, и спасибо большое Вам за терпение. Добавлено через 24 минуты И у меня ещё вопросик. Смотрите, инфы статуса выстрела для того чтобы был выбран стиль - недостаточно. Например, поле игрока, где корабли показаны, а не спрятаны отличается от того, поля, где корабли скрыты. Корабли отображаются таким образом, что... В общем, если это "моё" поле, то клетка без корабля - голубая. Если на ней корабль, то клетка зеленого цвета. Но, если поле не "моё", то даже, если там есть корабль, то клетка всё равно должна быть голубая. Означает ли это, что мне нужно будет забиндить (и предварительно создать) свойство в viewModel HideShips с чем-нибудь во view и потом использовать этот флаг в конвертере типа: если спрятать, то давай голубой, а если нет, то тащи зеленый. Так?
0
|
Модератор
|
|
29.05.2019, 14:22 | 26 |
Если клеточка это кнопка, то - да.
А в параметрах команды надо передавать контекст данных клеточки, То есть просто {Binding} Команда получает это контекст и из него извлекает координаты. Так как команда в параметрах получает object , а методу модели нужны int координаты, то сначала, как я уже написал, команды извлекает из параметра координаты.И что такое статус? Для выстрела это, наверное, bool - попал не попал? Так как от этого зависит право на следующий выстрел.Нет! Ответ от выстрела VM нужен для того чтобы определить кто следующий стреляет. Так как Модели это безразлично, то распределением очереди занимается VM. А вот изменение статуса Клеточки должно происходить по событийной схеме. Модель изменяет статус Клеточки, Клеточка через событие извещает своих подписчиков об изменении. Так как View является подписчиком INPC, то View обрабатывает изменение статуса. Конвертер здесь, наверное, не пригодится. Здесь должно хватить возможностей стиля с триггерами. View - это ОТОБРАЖЕНИЕ данных. Клеточка ОТОБРАЖАЕТ данные, соответствующего типа, допустим, Cell . У этого типа есть свойства необходимые для его идентификации и состояния, в том числе строка, колонка, статус и др.Посмотрите для примера класс Cell здесь Суммирование элементов в 2048 Когда создаётся команда её будет передаваться параметр, если параметр будет привязан к контексту данных {Binding} , то в методе обрабатывающем команду в параметре будет объект Cell и из него можно будет получить нужные данные.Так с ходу сказать невозможно. Надо сначала спроектировать приложение. Потом создать Model и интерфейс для View. И VM будет реализовывать этот интерфейс. То что сейчас очевидно для реализации с двумя VM это:
Возможно нужны будут для более красивой визуализации и информативности
Дальше надо смотреть по ходу реализации
1
|
Модератор
|
|
29.05.2019, 14:29 | 27 |
Нет!
Это возможность для хака. В варианте с двумя VM Модель отдаёт разные данные в разные VM. Для VM Cell на Sea противника до попадания - это пустой контент (свойство Content). После попадания в Content Model записывает Deck с состоянием Destroyed .
0
|
Модератор
|
|
29.05.2019, 14:35 | 28 |
Pro100Tom, у меня уже путаница в голове от предполагаемых различных вариаций.
Для таких подробностей нужно уже проектировать приложение и решать вопросы уже в конкретной реализации, а то только путаницу разведём.
0
|
154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
|
|
29.05.2019, 15:57 [ТС] | 29 |
Всё, я вроде бы всё понял наконец-то. 2048 прост спасло мне жизнь. Я теперь врубился, что значит "отображение данных" в данном контексте.
У меня есть ещё один вопрос и скорее всего после этого я отстану. Как я понимаю, изменения вида происходят по триггерам, как вы реализовали в 2048. А также написали выше следующее: Может ли (а точнее допустимо ли в конструкции MVVM) view быть подписчиком модели? Смотрите, моя модель координаты не имеет свойств кроме как row и column. В ней нет свойста WasChecked или HasShip, потому что как бы и не надо. Могу ли я забиндить некоторые свойства стиля моей клеточки триггером со свойством модели корабля? Например, у меня есть корабль и его свойство IsSunk. И триггер на background клетки поставить, что мол поменяй цвет на чёрный, если корабль потонул? Я задаю этот вопрос потому что я понимаю что в изоляции клетку можно забиндить с чем угодно, но в этом контексте она уже забиндина с моделью Cell. Или если биндинг уже есть, то свойство забиндить с чем-то другим уже нельзя и в таком случае в модели Cell мне придётся впихивать свойства WasChecked, HasShip, IsShipSunk?
0
|
Модератор
|
|
29.05.2019, 16:04 | 30 |
Не совсем понятно как у Вас соотносятся
Cell и корабль .Моей проекте моей Model тип Cell имел свойство Content в котором мог содержать палубу корабля Deck . Поэтому все привязки создавались без проблем.А у Вас как связаны Cell и корабль ?
0
|
154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
|
|
29.05.2019, 16:10 [ТС] | 31 |
У меня корабль имеет список of Cells. Когда мой VM будет проверять статус выстрела (мимо/ранил/убил), то в сервисе я логику описал так, что мы берем объект моря, берем в нём корабли и смотрим, есть ли корабль, в котором есть эта Cell. Если да, то надо еще проверить ранен ли корабль или уже убит, для этого у него реализовано свойство (или функция, не помню) которое возвращает флаг - мол, да, убит. Свойство "убит" важно, потому что я хочу чтобы, раненые корабли отображались оранжевым цветом, а вот потонувшие - меняли бы свой цвет (всех соответствующих креточек) на черный. А свойство "убит" должно находиться именно в модели класс Ship, а не Cell, имхо.
0
|
Модератор
|
|
29.05.2019, 16:50 | 32 |
Pro100Tom, Из-за начальной неправильно проектирования у Вас неверно распределены функции для VM и Model.
То что Вы описывает - может находиться в Model. Так как она обрабатывает свои данные удобным для себя образом не взирая на то как эти данные потом будут отображаться. Функция же VM - это преобразование данных Model в удобный для View вид. В данном случае, View у Вас отображает клеточки Cell. Этот тип должен предоставлять все данные необходимые его для отображения. Так Cell у Вас - это класс Model и не содержит всех требуемых свойств, то значит VM должна объявить свой тип для Cell и переносить данные в него из Model.Cell. Но это общий подход. В таком маленьком приложении как у Вас. Когда всё в одних руках, то проще изменить Model. На мой взгляд, Вы из-за недостатка опыта неверно спроектировали приложение. Поэтому у Вас неверно реализована Model - если она есть, вообще. Вы, по всей видимости, начали реализацию с View - отсюда и множество нестыковок.
0
|
154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
|
|
29.05.2019, 17:03 [ТС] | 33 |
Блин, вот не соглашусь с Вами. Cell - это клетка. Её задача хранить координаты. А Ship - это объект который мы ищем, играя в игру. Как тут можно смешать клетку и корабль в один винегрет-то? У меня корабли есть различных форм (нетолько прямые), я описал матрицу их поворота и отражения, описал логику расставления. Я не согласен с тем, что только потому что у меня проблема с биндингом, я не должен создавать ship как отдельный класс. Не хочу я менять модель клетки. Клетка не должна знать ничего о кораблях. Пройдёт время, я захочу добавить мины в игру, и что, мне придётся тогда менять снова класс клетки?
Я выбрал WPF потому что мне нравится визуализация. На биндинги мне плевать. Если использование WPF подразумевает использование биндингов - здорово, у меня есть повод изучить это и реализовать код используя эти биндинги. Но, если структура с биндингами заставялет меня смешивать два класса в одну кашу, извините - к чёрту биндинги. Я понимаю, что WPF не для морского боя создан. Так что, ещё разочек, повторю вопрос. Есть ли возможность создать Background свойство клетки со свойством корабля, если клетка уже связана с моделью Cell? Если нельзя, то норм ли имплементация где я в своей ViewModel создаю свойство, которое будет "следить" за свойством корабля IsSunk, и это свойство (класса ViewModel) будет забиндино с Background свойством моей клеточки (и то тут будет проблема одна связанная с выбором кол-ва экземпляров класса VM)? Если ответ нет, то стоит ли забить на биндинги и просто удалить гланды через задницу?
0
|
Модератор
|
|
29.05.2019, 18:16 | 34 |
Вы описываете поведение MODEL.
А типы для View имеют совсем иное назначение. Эти типы предназначены для ОТОБРАЖЕНИЯ. И если не хотите менять MODEL, то это Ваше видение - делайте так. Но тогда надо из типов Модели делать преобразование в типы View - это одна из основных функций VM. VM получает от Модели Cell и Ship и преобразует их уже в удобный для View вид - это тип клетки (назовите его - CellView) в котором содержится вся необходимая для отображения информация. Это функция VM. Допустим Model - это WEB сервер. И он обменивается с VM текстовыми сообщениями в формате JSON. И что теперь этот JSON тоже WPF должен отображать? Нет! VM преобразует полученные JSON в типы для View. Это то для чего и предназначена VM. У Вас вся путаница из-за того что большая часть функций Модели возложена на VM. И в результате VM не делает того собственно для чего она предназначена. View не может быть связанной с Моделью! VM, если не нужно преобразование, может напрямую передавать данные Модели прямо в её типах. Но такое возможно только в локальных приложениях. В общем же случае, перед передачей данных, VM их конвертирует в удобную для View форму. Да, это нормально. Надо использовать те инструменты которые есть и тем образом для которого они предназначены. Эти инструменты появились не на пустом месте - за ними десятки лет опыта миллионов программистов. Если они для чего-то не подходят, то это, в первую очередь, вопрос к себе А на верном ли я пути? .Конечно, как и любое творение рук человеческих, они не совершены. Но прежде чем пытаться их изменять или использовать их не по назначению, надо научиться их использовать тем образом для которого они предназначены. И уже потом делать выводы.
1
|
29.05.2019, 18:16 | |
29.05.2019, 18:16 | |
Помогаю со студенческими работами здесь
34
свойства get;set; Свойства set get Свойства get set Биндинг свойства контрола к одной из переменных свойства класса Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |