Новичок - путают о привязке и ресурсах в 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, однако они вообще не помогают.
Ответы
Ответ 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. В этом случае свойство является обязательным свойством "Источник".
Это основы. Надеюсь, вы найдете это полезным