Что самое худшее в WPF?

Я начал составлять себе список "WPF gotchas": все, что меня беспокоило, и что мне пришлось записывать, чтобы помнить, потому что я каждый раз падаю на них...

Теперь, я уверен, что вы все наткнулись на подобные ситуации в какой-то момент, и я хотел бы, чтобы вы делились своим опытом по этому вопросу:

Что такое gotcha, которая постоянно вас привлекает? тот, который вы считаете наиболее раздражающим?

(У меня есть несколько вопросов, которые кажутся без объяснения причин, возможно, ваши материалы объяснят их)

Вот несколько моих "личных" getchas (случайным образом):

  • Для запуска MouseEvent, даже если щелчок находится на "прозрачном" фоне элемента управления (например, метки), а не только на содержимом (в этом случае текст), фон управления должен установите значение "Brushes.Transparent", а не просто "null" (значение по умолчанию для метки)

  • WPF DataGridCell DataContext - это RowView, к которому принадлежит ячейка, а не CellView

  • Когда внутри ScrollViewer панель прокрутки управляется самим scrollviewer (т.е. параметры настройки, такие как ScrollBar.Value, не имеют эффекта)

  • Key.F10 не запускается, когда вы нажимаете "F10", вместо этого вы получаете Key.System, и вам нужно искать e.SystemKey, чтобы получить Key.F10

... и теперь вы находитесь.

Ответы

Ответ 1

  • Всегда смотрите окно вывода для ошибки привязки. Игнорирование вывода окно - рецепт слез.

  • Использовать PresentationTraceOptions.TraceLevel = "Высокий" в привязке для получения подробной информации привязки при отладке сбоев привязки.

  • Создание статических, неизменных ресурсов, таких как кисти PresentationOptions: Freeze = "True" для сохранения ресурсов во время выполнения.

  • Используйте WPF DataGrid в качестве datagrid. Модифицировать его, чтобы вести себя как Excel, - это огромная боль в прикладе.

  • BindingList<T> не очень хорошо работает с CollectionViewSource. Выведите ObservableCollection<T> из своих моделей просмотра.

  • Интернет предоставляет полдюжины различных идей для отображения текста CueBanner в текстовом поле WPF. Все они сломаны.

Ответ 2

1) Тот, который использовал меня каждые полчаса, когда я делал переход от WinForms: используйте TextBlock вместо Label, когда помещая случайный текст в пользовательский интерфейс (или вообще не использовать какой-либо тэг, если текст статичен)!

2) DataTriggers/Triggers не могут быть введены в Control.Triggers, но должны войти в Control.Styles/Style/Style.Triggers

3) Тип свойства должен реализовывать IList, а не IList<T>, если свойство должно быть признано XAML как свойство коллекции.

4) Исключения захвата привязок.

5) Используйте одноконвертерные преобразователи/статический преобразователь, поэтому вам не нужно создавать новый конвертер каждый раз, когда вы его используете.

6) Должен быть четко определен тип значения DependencyProperty по умолчанию: 0u как uint, (float) 0 как float, 0.0 как double...

7) Важно, чтобы определения свойств управления были до или после его содержимого.

8) НИКОГДА не используйте PropertyMetadata для установки значения по умолчанию ссылочного типа DependencyProperty. Эта же ссылка на объект будет назначена всем экземплярам класса-владельца.

Ответ 3

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

  • Списки не обновляются из-за забывания для использования ObservableCollection
  • Свойства не обновляются забыв добавить OnPropertyChanged или неверно набрав свойство Имя

Недавно я столкнулся с этими проблемами

Ответ 4

Всплывающие подсказки и ContextMenus не используют DataContext своего владельца? Я думаю, что сначала все получают

Ответ 5

  • Если включено, Button.IsCancel присваивает false Window.DialogResult, но Button.IsDefault нет.
    Они настолько похожи, и для меня сначала казалось интуитивным, что оба должны закрыть диалог. Я обычно нарушаю MVVM и исправляю это в коде

  • Button.IsCancel + Command= Диалог не будет закрыт (Window.DialogResult осталось неназначенным), но Command выполняет Насколько я понимаю: если IsCancel имел более высокий приоритет, чем Command, то в Esc он назначил 'false' на DialogResult и Command не будет вызываться. Или, если Command будет иметь более высокий приоритет, тогда он будет вызываться первым и DialogResult будет назначен. Я не понимаю, как это пропущено?

  • Переплетные исключения ласточек!
    Он не только крадет время во время отладки, но и ошибочен с точки зрения ООП, потому что, если выбрано исключение, это означает, что что-то исключительное произошло где-то в нашей системе (что-то от неправильного источника данных до несанкционированного доступа к сбою памяти), так что это может быть обрабатывается, только если вы знаете, что делать. Вы не можете просто catch(Exception){} ловить все, а затем игнорировать. Если в программе есть неизвестное исключение, оно должно уведомить пользователя, войти и закрыть не делать вид, будто все в порядке...

  • HeaderContent может иметь только один дочерний элемент управления и не имеет дополнения У всех должно быть дополнение даже логических элементов управления (контейнеров), правильно? Я думаю, что это непоследовательно. Как вы думаете?

  • Если вы установите фокус на ListBox через FocusManager.FocusedElement, вы все равно не сможете переключить его содержимое на клавиатуру, потому что для фокуса установлено значение ListBox es frame, а не контент. Я думаю, что я не знаю другого интерфейса API, который бы отображал что-то вроде рамки управления для программиста UI, и он должен быть инкапсулирован из нас, потому что абстрактно ListBox представляет список, это всего лишь список вещей, а не список вещей в коробке. нормально, у него есть поле в его имени, но все же... У нас почти два разных элемента управления. MVVM не нарушает исправления

  • ListBox.IsSynchronizedWithCurrentItem по умолчанию - false, поэтому если вы назначаете другое значение или null - ItesSource, то SelectedItem все еще сохраняет старое значение, пока пользователь не выберет что-то из нового списка. Например, это может испортить CanExecute. Нужно устанавливать его каждый раз вручную.

  • Отсутствие привязки, отображаемое в PasswordBox, приводит к тратам времени и грязным взломам... Но все же у него есть свойство string PasswordBox.Password, поэтому не пытайтесь спорить о безопасности, потому что Snoop...

  • Это не gotcha, но таблица выглядит так IE6 IMO. Контейнерный дизайн помогает разделить контент с его макетом.
    Поскольку каждый раз, когда мне нужно что-то менять, мне нужно испортить Grid.Row и Grid.Column. Да, у нас есть DockPanel, StackPanel и другие, но вы не можете выполнить выравнивание столбцов внутри них. (И DockPanel походит на полностью раздельный gotcha) Если UniformGrid будет более настраиваемым, то это будет идеально, я думаю. Вам всегда нужно выбирать между сеткой и панелями, и обычно, если вы получаете что-то, вы теряете что-то еще.

Ответ 6

На прошлой неделе я получил довольно изящный результат:

При создании шаблона RichTextBox обработка событий внутри шаблона выполняется по странному маршруту, который не имеет ничего общего ни с туннелированием, ни с пузырьками

Например: в случае события, которое предполагается туннелировать: событие сначала туннелирует через ContentPresenter, затем оно туннелируется с верхней части шаблона.

см. мой вопрос по теме

Ответ 7

Нет чистого способа обработки валидации в WPF, я не являюсь поклонником магической строки, которую по умолчанию предлагает IDataErrorInfo:

  public string this[string columnName]
  {
        if (columnName == "FirstName")
        {
            if (string.IsNullOrEmpty(FirstName))
                result = "Please enter a First Name";
        }
  }

Тем не менее, я пробовал много фреймворков, таких как SimpleMVVM, FluentValidation и MVVMValidation, а BY FAR MVVM Validation - лучшее, что можно сделать, например:

Validator.AddRule(() => RangeStart,
              () => RangeEnd,
              () => RuleResult.Assert(RangeEnd > RangeStart, "RangeEnd must be grater than RangeStart");

Ответ 8

Мой личный фаворит:

public double MyVariable
{
    get { return (double)GetValue(MyVariableProperty); }
    set { SetValue(MyVariableProperty, value); }
}
public static readonly DependencyProperty MyVariableProperty = DependencyProperty.Register(
    "MyVariable", typeof(double), typeof(MyControl), new UIPropertyMetadata(0));

Попробуйте, как только это свойство будет объявлено, оно сработает. Зачем? Потому что 0 не может быть присвоено двойному с использованием отражения, по-видимому.

Не правда, но есть совет: используйте Snoop или что-то подобное, если вы его не используете, вы, должно быть, сумасшедшие... Сумасшедший, я говорю ya!

Ответ 9

Binding.StringFormat работает только в том случае, если тип целевого свойства string.

Ответ 10

  • Свойство TreeView SelectedItem не может быть установлено. Вместо этого вы должны привязать свойство TreeViewItem IsSelected к вашей модели представления элемента и установить там свой выбор.

  • ListBox SelectedItem, с другой стороны, устанавливается, но выбор элемента не равен фокусу элемента. Если вы хотите реализовать правильную навигацию по клавиатуре вместе с выбором элементов в рамках модели представления, вам необходимо реализовать ручное исправление фокуса, например:

    public void FixListboxFocus()
    {
        if (lbFiles.SelectedItem != null)
        {
            lbFiles.ScrollIntoView(lbFiles.SelectedItem);
            lbFiles.UpdateLayout();
    
            var item = lbFiles.ItemContainerGenerator.ContainerFromItem(viewModel.SelectedFile);
            if (item != null && item is ListBoxItem listBoxItem && !listBoxItem.IsFocused)
                listBoxItem.Focus();
        }
    }
    

    ... и вызывать его каждый раз, когда вы меняете выбранный элемент из viewmodel:

    SelectedFile = files.FirstOrDefault();
    viewAccess.FixListboxFocus();