четверг, 11 марта 2010 г.

Отключение PresentationTraceSources в WPF

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

System.Windows.Data Error: 4 : Cannot find source for binding with reference …
System.Windows.Data Error: 39 : BindingExpression path error: …

и тому подобное. Это "работает" класс PresentationTraceSources. Подробнее о нём можно узнать в статьях Trace sources in WPF и How can I debug WPF bindings?.

Я расскажу не о том, зачем нужен этот класс и не о том, как им пользоваться, а о том, как же его "отключить", то есть как добиться того, что бы в Output не писалось то, что вам, может быть, и не нужно.

Самый простой способ отключения вывода PresentationTraceSources - програмный:

Action<TraceSource> disable = traceSource => traceSource.Switch.Level = SourceLevels.Off;
disable(PresentationTraceSources.AnimationSource);
disable(PresentationTraceSources.DataBindingSource);
disable(PresentationTraceSources.DependencyPropertySource);
// … И так далее по всем имеющимся TraceSource-ам

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

Правильный путь: воспользоваться файлом конфигурации. Но и следующее решение не будет работать:

<configuration>
  <system.diagnostics>
    <sources>
      <source name="System.Windows.Data" switchValue="Off" />
      <!-- и так далее … -->
    </sources>
  </system.diagnostics>
</configuration>

при запуске программы из-под отладчика, из-за того, что внутри PresentationTraceSources при создании экземпляра TraceSource проверяется, не подключён ли отладчик, и если подключён, и для switchValue указано значение Off, то будет использоваться значение SourceLevels.Warning.

Зная вышесказанное, не сложно исхотриться так:

<source name="System.Windows.Data" switchValue="Critical" />
<!-- и так далее … -->

где Critical - самый строгий уровень вывода, но отличный от Off. На практике ни одного сообщения с критическим уровнем важности я ещё не встечал, поэтому в ситуациях, когда хочется по возможности максимально избавиться от ненужного вывода, можно воспользоваться описанным советом.

На последок, полный пример с небольшой универсализацией, позволяющей задавать уровень вывода PresentationTraceSources один раз:

<configuration>
  <system.diagnostics>
    <sources>
      <source name="System.Windows.Data" switchName="PresentationTraceSwitch" />
      <source name="System.Windows.DependencyProperty" switchName="PresentationTraceSwitch" />
      <source name="System.Windows.Documents" switchName="PresentationTraceSwitch" />
      <source name="System.Windows.Freezable" switchName="PresentationTraceSwitch" />
      <source name="System.Windows.Interop.HwndHost" switchName="PresentationTraceSwitch" />
      <source name="System.Windows.Markup" switchName="PresentationTraceSwitch" />
      <source name="System.Windows.Media.Animation" switchName="PresentationTraceSwitch" />
      <source name="System.Windows.NameScope" switchName="PresentationTraceSwitch" />
      <source name="System.Windows.ResourceDictionary" switchName="PresentationTraceSwitch" />
      <source name="System.Windows.RoutedEvent" switchName="PresentationTraceSwitch" />
      <!-- Для 3.5 указано всё, что есть -->
    </sources>
    <switches>
      <!-- Do not use an "Off", because under debugger it's replaced to "Warning". -->
      <add name="PresentationTraceSwitch" value="Critical" />
    </switches>
  </system.diagnostics>
</configuration>

И не забудьте где-либо в коде вашей программы вызвать

PresentationTraceSources.Refresh();

без вызова метода Refresh() значения не будут зачитаны из конфигурационного файла.