Нашли ошибку или опечатку? Выделите текст и нажмите

Поменять цветовую

гамму сайта?

Поменять
Обновления сайта
и новые разделы

Рекомендовать в Google +1

Множественные привязки

63
1

В WPF в одном элементе управления разрешается использовать сколько угодно привязок. Можно модифицировать пример из предыдущей статьи, добавив к элементу TextBlock еще несколько привязок:

<StackPanel>
            <Slider Name="sld" Minimum="1" Maximum="72" Margin="10"
                TickFrequency="5" TickPlacement="BottomRight" Value="12"></Slider>
            <TextBox Name="txt" Margin="10" MinHeight="26" VerticalContentAlignment="Center"></TextBox>
            <ListBox Margin="10" Name="lst">
                <ListBoxItem Tag="DarkBlue">
                    <Label>Dark Blue</Label>
                </ListBoxItem>
                <ListBoxItem Tag="Blue">
                    <Label>Blue</Label>
                </ListBoxItem>
                <ListBoxItem Tag="LightBlue">
                    <Label>Light Blue</Label>
                </ListBoxItem>
            </ListBox>
            <!-- Используем несколько привязок -->
            <TextBlock Name="txb" Margin="10,5,0,10" 
                       FontSize="{Binding ElementName=sld, Path=Value, Mode=TwoWay}"
                       Text="{Binding ElementName=txt, Path=Text}"
                       Foreground="{Binding ElementName=lst, Path=SelectedItem.Tag, Mode=OneWay}"></TextBlock>
</StackPanel>
Использование нескольких привязок

Допускается также реализовать привязку данных. Например, можно создать выражение привязки для свойства TextBox.Text, связывающее его со свойством TextBlock.FontSize, которое содержит выражение привязки, связывающее со свойством Slider.Value. В этом случае, когда пользователь перетаскивает ползунок в новую позицию, значение передается от Slider в TextBlock и затем из TextBlock в TextBox.

Хотя все работает прозрачно, более ясный подход состоит в том, чтобы привязать элементы как можно ближе к данным, которые они используют. В описанном здесь примере необходимо предусмотреть привязку и TextBlock, и TextBox непосредственно к свойству Slider.Value.

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

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

Чтобы понять, как это работает, рассмотрим вариацию примера элемента — панели с ползунком, который включает текстовое поле, куда можно поместить точное значение размера шрифта. В этом примере свойство TextBlock.FontSize может быть установлено двумя путями: перетаскиванием ползунка или вводом в текстовом поле размера шрифта. Все элементы управления синхронизированы так, что если вводится новое число в текстовом поле, размер шрифта текста примера изменяется и ползунок перемещается в соответствующую позицию.

Как уже упоминалось, к свойству TextBlock.FontSize можно применять только одну привязку данных. Поэтому имеет смысл оставить свойство TextBlock.FontSize в том виде, как оно есть, чтобы оно привязывалось прямо к ползунку.

Хотя добавить другую привязку к свойству FontSize нельзя, можно привязать новый элемент управления TextBox к свойству TextBlock.FontSize. Ниже показана необходимая для этого разметка:

<TextBox Name="txt" Margin="10" MinHeight="26" VerticalContentAlignment="Center"
        Text="{Binding ElementName=txb, Path=FontSize, Mode=TwoWay}"></TextBox>
Привязка двух свойств

Теперь при каждом изменении свойства TextBlock.FontSize текущее значение будет вставляться в текстовое поле. Более того, значение в текстовом поле можно редактировать, применяя указанный размер шрифта. Обратите внимание, что для того, чтобы пример работал, свойство TextBox.Text должно использовать двунаправленную привязку, передающую данные в обоих направлениях. В противном случае текстовое поле сможет отображать значение TextBlock.FontSize, но не позволит изменять его.

С этим примером связано несколько нюансов:

  • Поскольку значение свойства Slider.Value имеет тип double, при перетаскивании ползунка получается дробное значение размера. Установив свойство TickFrequency в 1 (или в некоторый целочисленный интервал), a свойство IsSnapToTickEnabled в true, можно ограничить значение ползунка только целыми величинами.

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

  • Изменения, которые вносятся в текстовое поле, не будут применены до тех пор, пока текстовое поле не потеряет фокус (например, когда с помощью клавиши <Tab> происходит переход на другой элемент управления). Если такое поведение не подходит, можно обеспечить непрерывное обновление, используя свойство UpdateSourceTrigger объекта Binding.

Интересно, что показанное здесь решение — не единственный способ привязки текстового поля. Столь же разумно конфигурировать текстовое поле таким образом, чтобы оно изменяло значение свойства Slider.Value вместо свойства TextBlock.FontSize:

<TextBox Name="txt" Margin="10" MinHeight="26" VerticalContentAlignment="Center"
           Text="{Binding ElementName=sld, Path=Value, Mode=TwoWay}"></TextBox>

Теперь изменение текстового поля инициирует изменение положения ползунка, которое затем установит новый размер шрифта текста. Опять-таки, данный подход работает только с двунаправленной привязкой данных.

И, наконец, можно поменять местами роли ползунка и текстового поля, чтобы ползунок привязывался к текстовому полю.

В случае привязки Slider.Value текстовое поле ведет себя несколько иначе, чем в предыдущих двух примерах. Любые изменения, которые вносятся в текстовое поле, применяются немедленно, вместо того, чтобы ожидать момента утери фокуса.

Как видно из примера, двунаправленные привязки обеспечивает значительную гибкость. Их можно использовать для применения изменений от источника к цели и от цели к источнику. Допускается их применение в комбинации, что позволяет создать неожиданно сложные окна без какого-либо кода.

Обычно решение относительно того, куда применять выражение привязки, диктуется логикой модели кодирования. В предыдущем примере, возможно, было бы больше смысла поместить привязку в свойство TextBox.Text вместо свойства Slider.Value, потому что текстовое поле — это необязательное дополнение к вполне готовому примеру, а не основной ингредиент, на который полагается ползунок.

Также имело бы больше смысла привязать текстовое поле непосредственно к свойству TextBlock.FontSize вместо свойства Slider.Value. (Концептуально вы заинтересованы в том, чтобы видеть текущий размер шрифта, и ползунок — только один из способов его установки. Даже несмотря на то, что положение ползунка совпадает с размером шрифта, это — необязательная дополнительная деталь, если вы пытаетесь написать максимально ясную разметку.) Конечно, эти решения субъективны и определяются стилем кодирования. Наиболее важный урок состоит в том, что три подхода могут обеспечить одинаковое поведение.

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

Alexandr Erohin ✯ alexerohinzzz@gmail.com © 2011 - 2017