0

пытаюсь постигнуть MVVM. У меня есть поля и свойства(подключенные к XAML форме приложения)

В классе MainWindowViewModel:ViewModel

    private double _dL;
    public double dL
    {
        get => _dL;
        set => Set(ref _dL, value);
    }

Соотвественно в разметке XAML привязано поле dL.

Класс ViewModel реализуется так:

 internal abstract class ViewModel : INotifyPropertyChanged, IDisposable
{
    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;
}
public void Dispose()
{
    Dispose(true);
}
private bool _Disposed;
protected virtual void Dispose(bool Disposing)
{
    if (!Disposing || _Disposed) return;
    _Disposed = true;
}

}

Я раньше думал, что поля мне служат для внутренней логики и расчетов, а свойства для отображения на форме. Например, мне внутри программы нужно провести точные расчеты в вывести на форме - округленные значения. я в классе MainWindowViewModel что то считаю с _dL

_dL = 3.14*255/360+0.1;

То есть получаю точные значения без округления, а затем для формы пишу

dL = Math.Round(_dL,3); // и на форме отобразиться округленное значение

Но проблема в том, что если мне в дальнейшем нужно будет опять использовать для расчетов _dL (точное значение) - оно уже исчезло, и у меня только округленное свойство есть.

Как быть? Кроме очевидного - там где мне надо снова - опять считать _dL ?

  • Читать и писать поля, которые предназначены для свойств нельзя. Никогда не используйте их вне геттера или сеттера самого свойства. Форматировать значение можно прямо в XAML {Binding dL, StringFormat=F3} – aepot Aug 23 '21 at 10:16
  • _dL нужен вам для того, чтоб не получить цикличный вызов свойства, что приведет к StackOverflowException. То есть, если бы вы указали, к примеру get => dL;, то при получении значения у свойства, оно пошло бы получать его у dL (у самого себя), что привело бы опять к вызову get и опять, и опять. Вот поэтому, в свойствах данные хранят в отдельном, приватном поле. Так что, расценивайте их как одно целое и обращайтесь лишь к свойству, ведь если вы обратитесь к полю, то вся логика, прописанная у свойства не будет выполнена, что в вашем случае приведет к отсутствию обновления данных у окна. – EvgeniyZ Aug 23 '21 at 10:21
  • @aepot Если сделать StringFormat=F3 - то на форме не красиво 0.000 остается, хотелось бы этого избежать – Евгений Федоров Aug 23 '21 at 10:24
  • @EvgeniyZ А в чем тогда смысл разделения логики и представления?) Я думал это именно для того, что я описал в вопросе. а оказывается нет... То есть единственный способ достичь того результата, который мне нужен - снова считать dL без округления? – Евгений Федоров Aug 23 '21 at 10:26
  • Если вам надо хранить где либо старое значение, то сделайте еще одно приватное поле. Если вам надо лишь отобразить в нужном виде значение, не затрагивая при этом само значение, то указывайте {Binding SomeProp, StringFormat = нужныйВид}, если это не позволит вам подобное сделать, то делайте конвертор и указывайте его также, в привязке. – EvgeniyZ Aug 23 '21 at 10:26
  • @EvgeniyZ То есть для каждой пары - свойство-поле - добавляю еще одно приватное поле - которое не буду нигде менять, а только использовать для расчетов? – Евгений Федоров Aug 23 '21 at 10:28
  • @ЕвгенийФедоров смысл разделения логики и представления - В удобстве? В тестируемости? В разделении тех же обязанностей? В производительности? Например, вы можете отдать разработку View другому человек, который совершенно не знает про то, что будет у вас в логике, ему надо лишь знать, что будет некое свойство с, допустим, процентом загрузки, он на может сделать ProgressBar и стилизировать его, не дожидаясь логики. Посмотрите например это, там все отдельно друг от друга, в своей библиотеки. – EvgeniyZ Aug 23 '21 at 10:34
  • @ЕвгенийФедоров То есть для каждой пары - свойство-поле - добавляю еще одно приватное поле - ну если вам надо хранить старые данные, да, вам нужен еще один объект, в который будете их помещать. Но еще раз подумайте про View часть, ваше округление легко сделать на его уровне... – EvgeniyZ Aug 23 '21 at 10:36
  • @EvgeniyZ Ну "В удобстве? В тестируемости? В разделении тех же обязанностей? В производительности?" - это всё конечно да, но я тут просто один работаю, погроммист-самоучка, некому даже код лишний раз глянуть) поэтому вот задаю такие странные(для нормальных программистов) вопросы)) Спасибо) – Евгений Федоров Aug 23 '21 at 10:39
  • @ЕвгенийФедоров Даже если вы один, вам все перечисленное может понадобиться в будущем. Вот взгляните на пример по ссылке выше, там все в отдельный dll и чтобы мне добавить туда новый функционал, достаточно реализовать еще одну dll, которая будет иметь свою новую логику, может даже свой вид, и зарегистрировать ее. А что было бы если у вас проект был тесно связан, к примеру вы хранили данные в View (myTextBox.Text = "SomeText")? Малейшее изменение вызывало бы у вас негативные чувства и кучу боли. Так что старайтесь делать все менее связанным друг с другом. – EvgeniyZ Aug 23 '21 at 10:45
  • @EvgeniyZ Ох, да, почитал по ссылке, попробую повторить на досуге, спасибо. Просто в данный момент программа уже достаточно большая, а если делать по уму, то это очень много переписывать. Пока что на это нет времени. Единственное что нужно, это сделать, чтоб программа внутри считала точные значения, а в XAML выводила округленные приятные глазу, так сказать. – Евгений Федоров Aug 23 '21 at 10:58
  • @ЕвгенийФедоров Ну я вам сказал как. Делаете конвертор (если StringFormat мало) и в нем прописываете все нужные округления, будет "приятный глазу вид". – EvgeniyZ Aug 23 '21 at 11:12
  • @EvgeniyZ не совсем понял, какой конвертор и как он должен работать? Это про вот такое? https://wpf-tutorial.com/ru/39/%D1%81%D0%B2%D1%8F%D0%B7%D1%8B%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85/%D0%BA%D0%BE%D0%BD%D0%B2%D0%B5%D1%80%D1%82%D0%BE%D1%80-%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B9-ivalueconverter/

    Через него как то получается для отображения в привязке делать одну конвертацию, а для работы в коде - исходные не округленные значения? Правильно?

    – Евгений Федоров Aug 23 '21 at 11:20
  • @ЕвгенийФедоров Да, про него. У него два метода Convert и ConvertBack, где первый преобразует значение свойства в "читаемое" для контрола, а второй наоборот, значение контрола преобразует обратно в нужный формат для свойства (например TextBox, пользователь написал "один", а вам надо получить 1). Вам надо лишь отобразить, то есть только Convert. Чтоб не захламлять XAML, можете использовать трюк, с MarkupExtension (как например показано тут). – EvgeniyZ Aug 23 '21 at 11:31
  • Подписываюсь под всеми комментами @EvgeniyZ, за то, как отображать данные должен отвечать интерфейс, а не вьюмодель. Конвертер может быть такой return value is double number ? number.ToString("0.###") : value.ToString(); либо без конвертера StringFormat=0.### можно попробовать. – aepot Aug 23 '21 at 12:13
  • @EvgeniyZ Да, сделал конвертер, именно то что нужно получилось. Точнее конвертер с параметром, там в некоторых местах надо округлять до разных знаков после запятой, а потом в разметке ΔL =

    Еще раз всем спасибо!!!

    – Евгений Федоров Aug 23 '21 at 12:59
  • @ЕвгенийФедоров Вот это вот <Run>&#916;L = </Run> в StringFormat перенесите, более читаемо и приятней будет. Хотя на вкус и цвет.. – EvgeniyZ Aug 23 '21 at 16:16
  • Mode=TwoWay - лишнее. Вы ж не к текстбоксу привязываетесь, где и данные могут меняться прямо в интерфейсе, оставьте Mode=OneWay или вообще уберите. – aepot Aug 24 '21 at 11:38
  • @aepot а на форме этот бокс используется как для вывода так и для ввода информации. то есть вручную туда вписывают значение для расчетов. – Евгений Федоров Aug 26 '21 at 06:01
  • Ну ок. Пусть будет так – aepot Aug 26 '21 at 06:04

0 Answers0