Используя Преобразователи Значения в WPF, не имея необходимость определять их как ресурсы сначала

Действительно ли возможно использовать преобразователи значения, не имея необходимость определять их заранее как ресурсы?

Прямо сейчас я имею

<Window.Resources>
    <local:TrivialFormatter x:Key="trivialFormatter" />
</Window.Resources>

и

<Button Width="{Binding Width, 
               ElementName=textBox1, 
               UpdateSourceTrigger=PropertyChanged, 
               Converter={StaticResource trivialFormatter}}">

Не был бы это быть возможным это вместо того, чтобы иметь необходимость объявить trivialFormatter ресурс в Окне. Ресурсы, я мог непосредственно отослать его от обязательной Кнопки width? Что-то как

Converter = {local:TrivialFormatter}

Спасибо

20
задан ΩmegaMan 26 October 2018 в 14:42
поделиться

4 ответа

Полностью не проверен:

public class ViewModelBuilderFactory
{
    public IViewModelBuilder GetViewModelBuilder (string docType, IRepository repository)
    {
        switch (docType)
        {
            case "ProgressNotes":
                return new ProgressNotesViewModelBuilder(repository);
            case "Labs":
                return new LabsViewModelBuilder(repository);
            default:
                throw new ArgumentException(
                    string.Format("docType \"{0}\" Invalid", docType);
        }
    }
}

public interface IViewModelBuilder
{
    TreeViewModel GetDocTreeViewModel();
    WorkSpace GetWorkSpace(Patient patient);
}

public class LabsViewModelBuilder : IViewModelBuilder
{
    private IRepository _repository;
    public LabsViewModelBuilder(IRepository repository)
    {
        _repository = repository;
    }

    public TreeViewModel GetDocTreeViewModel()
    {
        return new TreeViewModel(_repository.GetPatientLabs());
    }

    public Workspace GetWorkspace(Patient patient)
    {
        return LabViewModel.NewLabViewModel(patient);
    }
}

public class ProgressNotesViewModelBuilder : IViewModelBuilder
{
    private IRepository _repository;
    public ProgressNotesViewModelBuilder(IRepository repository)
    {
        _repository = repository;
    }

    public TreeViewModel GetDocTreeViewModel()
    {
        return new TreeViewModel(_repository.GetPatientProgressNotes());
    }

    public Workspace GetWorkspace(Patient patient)
    {
        return ProgressNoteViewModel.NewProgressNoteViewModel(patient);
    }
}

Теперь ваш вызывающий код:

ViewModelBuilderFactory factory = new ViewModelBuilderFactory();
IViewModelBuilder modelBuilder = factory.GetViewModelBuilder(docType, repository);
this.DocTreeViewModel = modelBuilder.GetDocTreeViewModel();
Workspace workspace = modelBuilder.GetWorkspace(patient);
this.Workspaces.Add(workspace);
this.SetActiveWorkspace(workspace);

[4 изменения с момента первого сообщения; продолжать видеть ошибки]

[Далее редактировать, отмечая, что вы используете Castle IOC]

В вашей конфигурации Castle xml можно добавить (и я работаю только над неопределенным знанием Castle здесь)

<component id="ProgressNotesViewModelBuilder"
           type="MyNamespace.ProgressNotesViewModelBuilder, MyAssembly">
    <parameters>
        <!-- reference to repository here -->
    </parameters>
</component>
<component id="LabsViewModelBuilder"
           type="MyNamespace.LabsViewModelBuilder, MyAssembly">
    <parameters>
        <!-- reference to repository here -->
    </parameters>
</component>

Тогда вам не нужен ViewModelBuilderFactory, вы можете просто заменить

IViewModelBuilder modelBuilder = factory.GetViewModelBuilder(docType, repository);

на

IViewModelBuilder modelBuilder = (IViewModelBuilder)
    container.Resolve(docType + "ViewModelBuilder");

Теперь вам вообще не нужна инструкция switch.

Однако стоит отметить, что переключатели не являются злом, они просто плохо пахнут и, как и все плохие запахи, должны быть изолированы от всего, что хорошо пахнет; это то, чего должна достичь образец Фабрики.

-121--4028580-

Проблема коренится в аппаратном обеспечении - каждый ЦП имеет различное поведение в отношении когерентности кэша, видимости памяти и переупорядочивания операций. Java имеет лучшую форму, чем C++, потому что она определяет кроссплатформенную модель памяти, на которую могут рассчитывать все программисты. Когда Java работает в системе, модель памяти которой слабее, чем требуется для Java Memory Model, JVM должен компенсировать разницу.

Такие языки, как C, «наследуют» модель памяти базового оборудования. Существует работа, чтобы дать C++ формальную модель памяти, чтобы программы C++ могли означать одно и то же на разных платформах.

-121--1603127-

В случае singleton-type IValureConverter (например, они не нуждаются в каком-либо состоянии из текущего экземпляра привязки) я использую статические преобразователи, то есть:

Converter={x:Static SomeNamespace:SomeConverter.Instance}

Есть также замечательный пост доктора WPF об использовании расширения разметки, чтобы сделать его более чистым в линии здесь .

25
ответ дан 29 November 2019 в 23:52
поделиться

Я не знаю способа сделать это так, как вы говорите, но я просто попробовал это в качестве примера, и это сработало. В вашем файле App.xaml.cs вы можете создать метод, который использует отражение для загрузки конвертеров.

private void Application_Startup(object sender, StartupEventArgs e)
{
    LoadConverters();
}

private void LoadConverters()
{
    foreach(var t in Assembly.GetExecutingAssembly().GetTypes())
    {
        if (t.GetInterfaces().Any(i => i.Name == "IValueConverter"))
        {
            Resources.Add(t.Name, Activator.CreateInstance(t));
        }
    }
}

Затем вы можете использовать конвертер вот так, половина пути пройдена.

<Button Width="{Binding Width, Converter={StaticResource TrivialFormatter}}" />

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

2
ответ дан 29 November 2019 в 23:52
поделиться

Технически я верю, что вы можете это сделать, но XAML настолько ужасен, что из-за него подход «много тривиальных ресурсов» будет выглядеть как рай простоты и ясность по сравнению:

<Button Height="50">
  <Button.Width>
    <Binding Path="Width" ElementName="textBox1" UpdateSourceTrigger="PropertyChanged">
      <Binding.Converter>
        <local:TrivialFormatter />
      </Binding.Converter>
    </Binding>
  </Button.Width>
</Button>

Я не тестировал это, потому что даже чтение вызывает слезы у меня в глазах ...

7
ответ дан 29 November 2019 в 23:52
поделиться

Я бы определенно изучил предложение Мики, которое включает использование статического одноэлементного экземпляра вашего конвертера. Но еще одна вещь, которую следует учитывать, заключается в том, что если вы используете отдельный шаблон представления, такой как MVVM, вы часто можете избежать необходимости в преобразователе значений, реализовав преобразование в ViewModel.

Есть много причин, по которым вы можете захотеть это сделать.

Во-первых, это гораздо более проверяемо. Ваши модульные тесты могут быть уверены, что все, что выходит из ViewModel, будет отображаться пользовательским интерфейсом. Вы можете представить себе тестирование требования о том, что долларовые значения должны соответствовать формату валюты текущего языка, использовать два десятичных знака и т. Д.

Еще одна веская причина состоит в том, что исключения в преобразователях значений , а не будут рассматриваться как ошибки проверки. что может быть огромной головной болью в Silverlight. Даже если вы установите для ValidatesOnExceptions значение true в привязке, если ваш преобразователь значений вызовет исключение, Silverlight просто позволит ему распространиться. Однако, если вы используете ViewModel для преобразования, исключение будет рассматриваться как ошибка проверки.

Обратной стороной является потеря возможности многократного использования преобразователя значений общего назначения.

6
ответ дан 29 November 2019 в 23:52
поделиться
Другие вопросы по тегам:

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