Подтвердить что ты не робот

Новичок - путают о привязке и ресурсах в WPF

Я пытаюсь изучить WPF, но мне очень сложно понять привязки, "ресурсы" и создание объектов. Мой фон находится в С++/MFC и С# -Winforms.

Мои вопросы:

  • Большинство примеров, которые я вижу в XAML (в MSDN и в двух других прочитанных WPF-книгах), используйте StaticResource в выражении привязки. Связаны ли они каким-либо образом с статическими членами? Или это просто вводящее в заблуждение имя? Когда ссылка делается на какой-либо объект как StaticResource, когда он создается?

  • Насколько я вижу, StaticResources используются с "вещами", определенными в разделе "Ресурсы" приложения/окна/элемента управления и т.д.
    Теперь эти разделы "Ресурсы" очень запутывают меня. Что это такое? По моему опыту в MFC это были значки, строки и т.д. Однако, судя по всем примерам, которые я видел, в WPF это, по-видимому, по сути является "свалкой" для (a) все виды определения глобальных объектов в разметке (стили, шаблоны данных и т.д.) (b) все виды глобальных экземпляров объектов в разметке Я прав? Мне это кажется очень грязным. Это, по сути, включает в себя изучение всех видов полу DSL в XAML (для определения стилей, для определения шаблонов данных, для создания объектов и т.д.) И объединения их в одном месте. Я продолжаю думать о чем-то вроде редактирования файла ресурсов (.rc) в MFC вручную. По крайней мере, разделы были хорошо разделены, а синтаксис для каждого ресурса был относительно простым.

  • Чтобы связать предыдущие два вопроса: когда я определяю экземпляр объекта в разделе "Ресурсы" , а затем ссылаюсь на него из привязки StaticResource, когда он точно создан? MSDN говорит (в разделе "Как сделать данные доступными для привязки в XAML" ):

один способ сделать объект доступный для привязки - это определить его как ресурс

Однако это не очень понятно. Что они означают? Они означают, что они созданы? Они означают подключение к подсистеме привязки? А когда именно этот объект создан? Из простого примера я увидел, что WPF, похоже, создает этот объект для меня, когда он пытается привязать привязку. И это еще более запутанно.

EDIT: После разъяснения кармическим приемом ниже я все еще смущен тем, как это связано с привязкой. Предположим, что у меня есть в моих ресурсах:

<local:Person x:Key="MyPerson" Name="Title"/> 

(где Person - класс с именем Name), а затем в окне:

<TextBlock Text="{Binding Source={StaticResource MyPerson}, Path=Name}"/> 

1) Что это делает? Проходит ли он через те же шаги - поиск ресурса, а затем его применение к свойству Text? Создается ли объект MyPerson во время создания окна или позже? 2) Должен ли я использовать механизм привязки для привязки к свойству Name? Не могу я привязаться к нему напрямую, как вы делали это с помощью myBrush? Почему я не могу сделать что-то подобное?

<TextBlock Text="{StaticResource MyPerson, Path=Name}"/> 

Это просто близорукость со стороны рамки? Я думаю, что здесь очень мало, но я не могу понять, что...

3) Я попытался использовать DynamicResource, но я очень смущен в каждом шаге, который я сделал.  a) Добавлен объект DependencyObject с DependencyProperty над моим единственным классом Window в коде (нужен ли этот DependencyObject?)

public class SomeText : DependencyObject
{
    public string Header
    {
        get { return (string)GetValue(HeaderProperty); }
        set { SetValue(HeaderProperty, value); }
    }
    public static readonly DependencyProperty HeaderProperty =
        DependencyProperty.Register("Header", typeof(string), typeof(SomeText), new UIPropertyMetadata(0));
}

b) Добавил экземпляр из него в Windows.Resources(это необходимо для DynamicResource? MSDN, похоже, говорит "нет", но если это так, я не могу понять, как сделать следующий шаг в XAML)

c) Я попробовал оба:

Text="{Binding Source={DynamicResource HeaderText}, Path=Header}"

Который дал мне исключение, и

Text="{DynamicResource HeaderText}"

Но я не мог понять, куда поместить путь к свойству Header.

Это моя пятая или около того попытка поиграть с WPF в последнее время, и каждый раз, когда я нахожусь в тупике по этим, казалось бы, простым вещам, которые не работают. Я прочитал 2 книги, и я действительно пытаюсь понять статьи MSDN, однако они вообще не помогают.

4b9b3361

Ответ 1

Во-первых, общий комментарий:

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

  • Как XamlReader (и, в частности, расширения разметки) десериализует XAML в объекты.
  • Как работают словари ресурса FrameworkElement.
  • Как работает привязка данных.

Что-то вроде этого:

<TextBox Text="{Binding Source={StaticResource MyPerson}, Path=Name}"/>

объединяет (по крайней мере) три очень разных технологии одновременно. Все эти технологии разработаны настолько гибкими, насколько это возможно, что только делает их более запутанными для новичков. Идея о том, что источник привязки может быть примерно чем угодно: это трудно понять. Идея о том, что расширение разметки является особым видом формата сериализации, который поддерживает рекурсию: достаточно простой, чтобы понять в принципе, но немного озадачен, когда вы впервые начинаете работать с реальными примерами. Идея о том, что словарь ресурсов может содержать что угодно, и что алгоритм поиска ресурсов существенно делает ресурсы наследуемыми: опять же, довольно простой в концепции, но легко потерять поток, когда вы пытаетесь выяснить привязку данных и XAML на в то же время.

Это расстраивает, потому что что-то концептуально простое - "Я хочу привязать этот элемент управления к свойству объекта, который я создал" - требует, чтобы вы поняли многое, прежде чем сможете реально выразить его в XAML.

Единственное решение - быть терпеливым и следить за тем, чтобы вы понимали вещи на самом низком уровне. Когда вы увидите это:

{StaticResource MyPerson}

вы должны подумать: "Это вызовет обработчик расширения разметки StaticResource, который извлекает объект из словаря ресурсов с помощью клавиши MyPerson, когда XAML десериализуется.

Это очень сложная задача. Я профессионально занимаюсь разработкой программного обеспечения в течение 35 лет, и я нашел WPF самой сложной технологической платформой, которую я когда-либо изучал с большой разницей. Но все это сложно изучать, потому что это невероятно функционально и гибко. И выигрыш обучения у него огромен.

Чтобы решить пару проблем, которые не выполняли karmicpuppet:

Из моего опыта в MFC [resources] были значки, строки и т.д.

Это не изменилось. Вы все равно можете создавать файлы ресурсов в WPF и загружать их в объекты во время выполнения. Существует множество способов сделать это: вы можете создавать ресурсы в редакторе ресурсов и загружать их через объект Properties.Resources, вы можете добавлять файлы изображений (например) в проект, скомпилировать их как ресурсы и загрузить их используя их URI, и есть много других способов, о которых я не знаю.

Ресурсы, доступные FrameworkElement через свои ресурсные словари, - это совсем другое. Ну, вроде. Вот пример:

<Window.Resources>
   <Image x:Key="MyImage" Source="images/myimage.png"/>
</Window.Resources>

Это создает объект Image и добавляет его в словарь ресурсов Window с ключом MyImage. Затем вы можете ссылаться на этот объект через расширение разметки StaticResource в XAML или метод FindResource в код.

Установка атрибута Source в элементе Image в XAML также позволяет XamlReader использовать ResourceManager для чтения данных изображения из скомпилированных ресурсов проекта во время выполнения, когда он создает объект Image.

На практике это не так странно, как когда вы впервые изучаете WPF. Я никогда не получаю ресурсы, которые загружают ResourceManager и ресурсы, хранящиеся в ресурсных словарях.

И когда именно этот объект создан?

Любой объект, определенный элементом XAML, создается, когда XamlReader считывает элемент. Итак:

<Window.Resources>
   <local:Person x:Key="MyPerson"/>
</Window.Resources>

создает новый объект Person и добавляет его в словарь ресурсов Window с ключом MyPerson. Это точно эквивалентно выполнению этого в коде Window:

AddResource("MyPerson", new Person());

Так почему бы вам просто не сделать это в коде? Две причины:

Во-первых, это непротиворечиво. Если вы определяете все свои ресурсы в XAML, вам нужно только посмотреть в файлах XAML, чтобы узнать, какие у вас ресурсы. Если вы определяете их как в XAML, так и в коде, вам нужно посмотреть в двух местах.

Во-вторых, IDE знает о ресурсах, которые вы определяете в XAML. Если вы наберете

<TextBox Text="{Binding {StaticResource MyPerson}, Path=Name}"/>

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

Ответ 2

Подумайте об этом так: все элементы FrameworkElements (Windows, кнопки, другие элементы управления и т.д.), а также объект Application содержат словарь ресурсов. Всякий раз, когда вы определяете ресурс в XAML, как показано здесь:

<Window>
  <Window.Resources>
    <SolidColorBrush x:Key="myBrush" Color="Red"/>
    <DataTemplate x:Key"myTemplate">
      <!--Template definition here -->
    </DataTemplate>
  </Window.Resources>
</Window>

Мне нравится делать что-то вроде этого в коде:

class Window
{
  void Window()
  {
    this.Resources.Add("myBrush", new SolidColorBrush(Brushes.Red));
    this.Resources.Add("myTemplate", new DataTemplate());
  }
}

Вы можете поместить все объекты в качестве "Ресурсы". Все, что вы хотели бы использовать повторно в своем приложении, вы можете определить его как ресурс.

Теперь, когда вы используете "{StaticResource}" следующим образом:

<Button Background="{StaticResource myBrush}"/>

Это похоже на указание WPF выполнить поиск соответствующего ресурса "myBrush" и применить его к свойству Background. Что произойдет, WPF сначала будет искать ресурс в словаре ресурсов Button, а если он не будет найден, будет искать его родительский элемент, его родительский родитель и т.д. До ресурсов приложения.

"Статическая" вещь в "StaticResource" просто отличает ее от другого типа поиска ресурсов, называемого "DynamicResource". Разница между двумя ответами предоставляется в этой ссылке.

При применении к Binding он также работает одинаково. Скажем, например, у вас есть следующий ресурс в вашем XAML:

<local:Person x:Key="MyPerson" Name="Title"/>

и использовал его как:

<TextBlock Text="{Binding Source={StaticResource MyPerson}, Path=Name}"/>

В этом случае произойдет что-то вроде этого:

Binding b = new Binding();
b.Source = FindResource("MyPerson");
b.Path = "Name";
[TextBlock].SetBinding(TextBlock.TextProperty, b);

Опять же, разметка "{StaticResource}" в XAML сообщает WPF искать соответствующий ресурс и устанавливать его как значение свойства a. В этом случае свойство является обязательным свойством "Источник".

Это основы. Надеюсь, вы найдете это полезным