Где сохранить параметры приложения/, указывают в приложении MVVM

Вот возможная процедура для достижения вашей цели:

  1. Запустите tesseract для входного изображения и создайте файл hocr.
  2. Разобрать файл hocr, используя модуль синтаксического анализа hocr, и получить извлеченные тексты в списке.
  3. Найдите нужное числовое поле в списке строк (тексты, прочитанные tesseract) с помощью регулярных выражений.
  4. Файлы Hocr также содержат ограничивающий прямоугольник для каждого извлеченного текста и, таким образом, получают ограничивающий прямоугольник для всего поля.
  5. Используйте ограничивающую рамку и размеры, чтобы создать маленькое изображение с белым фоном.
  6. Отметьте это , чтобы наложить меньшее изображение на большее изображение

26
задан Paul Alexander 24 April 2009 в 23:50
поделиться

3 ответа

If you weren't using M-V-VM, the solution is simple: you put this data and functionality in your Application derived type. Application.Current then gives you access to it. The problem here, as you're aware, is that Application.Current causes problems when unit testing the ViewModel. That's what needs to be fixed. The first step is to decouple ourselves from a concrete Application instance. Do this by defining an interface and implementing it on your concrete Application type.

public interface IApplication
{
  Uri Address{ get; set; }
  void ConnectTo(Uri address);
}

public class App : Application, IApplication
{
  // code removed for brevity
}

Now the next step is to eliminate the call to Application.Current within the ViewModel by using Inversion of Control or Service Locator.

public class ConnectionViewModel : INotifyPropertyChanged
{
  public ConnectionViewModel(IApplication application)
  {
    //...
  }

  //...
}

All of the "global" functionality is now provided through a mockable service interface, IApplication. You're still left with how to construct the ViewModel with the correct service instance, but it sounds like you're already handling that? If you're looking for a solution there, Onyx (disclaimer, I'm the author) can provide a solution there. Your Application would subscribe to the View.Created event and add itself as a service and the framework would deal with the rest.

9
ответ дан 28 November 2019 в 17:20
поделиться

Да, вы на правильном пути. Если у вас в системе есть два элемента управления, которым необходимо передавать данные, вы хотите сделать это максимально разъединенным образом. Есть несколько способов сделать это.

В Prism 2 у них есть область, похожая на «шину данных». Один элемент управления может создавать данные с ключом, который добавляется к шине, и любой элемент управления, который хочет, чтобы эти данные, могли регистрировать обратный вызов при изменении этих данных.

Лично я реализовал то, что я называю «ApplicationState». Это имеет ту же цель. Он реализует INotifyPropertyChanged, и любой в системе может писать в определенные свойства или подписываться на события изменения. Он менее универсален, чем решение Prism, но работает. Это в значительной степени то, что вы создали.

Но теперь, у вас есть проблема, как обойти состояние приложения. Старый способ сделать это - сделать его синглтоном. Я не большой поклонник этого. Вместо этого у меня есть интерфейс, определенный как:

public interface IApplicationStateConsumer
{
    public void ConsumeApplicationState(ApplicationState appState);
}

Любой визуальный компонент в дереве может реализовать этот интерфейс и просто передать состояние приложения в ViewModel.

Затем в корневом окне при возникновении события Loaded я пересекаю визуальное дерево и ищу элементы управления, которым требуется состояние приложения (IApplicationStateConsumer). Я передаю им appState, и моя система инициализируется. Это инъекция зависимости бедного человека.

С другой стороны, Prism решает все эти проблемы. Я бы хотел вернуться и перестроить архитектуру, используя Prism ... но мне уже слишком поздно, чтобы быть экономически эффективным.

Я не большой поклонник этого. Вместо этого у меня есть интерфейс, определенный как:

public interface IApplicationStateConsumer
{
    public void ConsumeApplicationState(ApplicationState appState);
}

Любой визуальный компонент в дереве может реализовать этот интерфейс и просто передать состояние приложения в ViewModel.

Затем в корневом окне при возникновении события Loaded я пересекаю визуальное дерево и ищу элементы управления, которым требуется состояние приложения (IApplicationStateConsumer). Я передаю им appState, и моя система инициализируется. Это инъекция зависимости бедного человека.

С другой стороны, Prism решает все эти проблемы. Я бы хотел вернуться и перестроить архитектуру, используя Prism ... но мне уже слишком поздно, чтобы быть экономически эффективным.

Я не большой поклонник этого. Вместо этого у меня есть интерфейс, определенный как:

public interface IApplicationStateConsumer
{
    public void ConsumeApplicationState(ApplicationState appState);
}

Любой визуальный компонент в дереве может реализовать этот интерфейс и просто передать состояние приложения в ViewModel.

Затем в корневом окне при возникновении события Loaded я пересекаю визуальное дерево и ищу элементы управления, которым требуется состояние приложения (IApplicationStateConsumer). Я передаю им appState, и моя система инициализируется. Это инъекция зависимости бедного человека.

С другой стороны, Prism решает все эти проблемы. Я бы хотел вернуться и перестроить архитектуру, используя Prism ... но мне уже слишком поздно, чтобы быть экономически эффективным.

когда происходит событие Loaded, я пересекаю визуальное дерево и ищу элементы управления, которым требуется состояние приложения (IApplicationStateConsumer). Я передаю им appState, и моя система инициализируется. Это инъекция зависимости бедного человека.

С другой стороны, Prism решает все эти проблемы. Я бы хотел вернуться и перестроить архитектуру, используя Prism ... но мне уже слишком поздно, чтобы быть экономически эффективным.

когда происходит событие Loaded, я пересекаю визуальное дерево и ищу элементы управления, которым требуется состояние приложения (IApplicationStateConsumer). Я передаю им appState, и моя система инициализируется. Это инъекция зависимости бедного человека.

С другой стороны, Prism решает все эти проблемы. Я бы хотел вернуться и перестроить архитектуру, используя Prism ... но мне уже слишком поздно, чтобы быть экономически эффективным.

2
ответ дан 28 November 2019 в 17:20
поделиться

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

С другой стороны, основываясь на вашем фактическом коде, может случиться так, что ApplicationViewModel имеет неправильное имя, он не является моделью доступным для представления, так что это может быть просто плохой выбор имени.

В любом случае, решение сводится к снятию ответственности. С моей точки зрения, у вас есть три вещи для достижения:

  1. Разрешить пользователю запрашивать подключение к адресу.
  2. Использовать этот адрес для подключения к серверу.
  3. Сохранить этот адрес.

Я бы посоветовал вам использовать три класса вместо двух.

public class ServiceProvider
{
    public void Connect(Uri address)
    {
        //connect to the server
    }
} 

public class SettingsProvider
{
   public void SaveAddress(Uri address)
   {
       //Persist address
   }

   public Uri LoadAddress()
   {
       //Get address from storage
   }
}

public class ConnectionViewModel 
{
    private ServiceProvider serviceProvider;

    public ConnectionViewModel(ServiceProvider provider)
    {
        this.serviceProvider = serviceProvider;
    }

    public void ExecuteConnectCommand()
    {
        serviceProvider.Connect(Address);
    }        
}

Следующее, что нужно решить - как адрес попадает в SettingsProvider. Вы можете передать его из ConnectionViewModel, как вы это делаете в настоящее время, но я не заинтересован в этом, потому что это увеличивает сцепление модели представления, и ViewModel не несет ответственности за знание того, что его необходимо сохранить. Другой вариант - позвонить из ServiceProvider, но мне не кажется, что это тоже обязанность ServiceProvider. На самом деле это не похоже на чью-либо ответственность, кроме SettingsProvider. Что приводит меня к мысли, что провайдер настроек должен прислушиваться к изменениям подключенного адреса и сохранять их без вмешательства. Другими словами, событие:

public class ServiceProvider
{
    public event EventHandler<ConnectedEventArgs> Connected;
    public void Connect(Uri address)
    {
        //connect to the server
        if (Connected != null)
        {
            Connected(this, new ConnectedEventArgs(address));
        }
    }
} 

public class SettingsProvider
{

   public SettingsProvider(ServiceProvider serviceProvider)
   {
       serviceProvider.Connected += serviceProvider_Connected;
   }

   protected virtual void serviceProvider_Connected(object sender, ConnectedEventArgs e)
   {
       SaveAddress(e.Address);
   }

   public void SaveAddress(Uri address)
   {
       //Persist address
   }

   public Uri LoadAddress()
   {
       //Get address from storage
   }
}

Это создает тесную связь между ServiceProvider и SettingsProvider, которого вы, по возможности, хотите избежать, и я бы использовал здесь EventAggregator, который я обсуждал в ответе на этот вопрос

Чтобы решить вопросы тестируемости, теперь у вас есть очень определенное ожидание того, что будет делать каждый метод. ConnectionViewModel вызовет соединение, ServiceProvider соединится, а SettingsProvider сохранится. Чтобы протестировать ConnectionViewModel, вы, вероятно, захотите преобразовать соединение с ServiceProvider из класса в интерфейс:

public class ServiceProvider : IServiceProvider
{
    ...
}

public class ConnectionViewModel 
{
    private IServiceProvider serviceProvider;

    public ConnectionViewModel(IServiceProvider provider)
    {
        this.serviceProvider = serviceProvider;
    }

    ...       
}

Затем вы можете использовать макетную среду для представления поддельного IServiceProvider, который вы можете проверить, чтобы убедиться, что метод connect был вызван с ожидаемыми параметрами.

Тестирование двух других классов более сложное, так как они будут полагаться на наличие реальный сервер и реальное постоянное устройство хранения. Вы можете добавить дополнительные уровни косвенности, чтобы отложить это (например, PersistenceProvider, который использует SettingsProvider), но в конечном итоге вы покидаете мир модульного тестирования и вступаете в интеграционное тестирование. Обычно, когда я кодирую с помощью шаблонов выше моделей и моделей просмотра, можно получить хороший охват модульных тестов, но поставщики требуют более сложных методологий тестирования.

Конечно,

11
ответ дан 28 November 2019 в 17:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: