#c# #wpf #xaml #data-binding
#c# #wpf #xaml #привязка к данным
Вопрос:
У меня есть кнопка внутри ItemsControl
(элемент управления видом разбивки на страницы) У меня есть свойство, названное current_page
в моей модели представления.
Я хочу сравнить current_page
значение с Content
Button
тем, что есть (1 / 2 / 3 ) и хотите изменить цвет переднего плана кнопки.
Пожалуйста, взгляните на код.
Моя ViewModel
public PaginationModel pagination
{
get { return _pagination; }
set { _pagination = value; OnPropertyChanged("pagination"); }
}
Моя модель разбивки на страницы
public class PaginationModel: INotifyPropertyChanged {
private int _total_items;
public int total_items {
get {
return _total_items;
}
set {
_total_items = value;
OnPropertyChanged("total_items");
}
}
private int _items_per_page;
public int items_per_page {
get {
return _items_per_page;
}
set {
_items_per_page = value;
OnPropertyChanged("items_per_page");
}
}
private int _current_page;
public int current_page {
get {
return _current_page;
}
set {
if (value <= total_pages 1 amp;amp; value > 0) {
_current_page = value;
OnPropertyChanged("current_page");
}
}
}
private int _total_pages;
public int total_pages {
get {
return _total_pages;
}
set {
_total_pages = value;
OnPropertyChanged("total_pages");
}
}
private ObservableCollection < string > _PageList;
public ObservableCollection < string > PageList {
get {
_PageList = new ObservableCollection < string > ();
for (int i = 0; i < total_pages; i ) {
_PageList.Add((i 1).ToString());
}
return _PageList;
}
set {
_PageList = value;
OnPropertyChanged("PageList");
}
}
}
Мой макет
<ItemsControl ItemsSource="{Binding pagination.PageList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0">
<Button Content="{Binding}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Command="{Binding DataContext.ChangePage, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
Width="20"
Margin="10,0"></Button>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Шаблон стиля кнопки
<Style TargetType="{x:Type Button}" >
<Setter Property="Foreground" Value="{StaticResource WordOrangeBrush}" />
<Setter Property="Background" Value="Transparent"></Setter>
<Setter Property="BorderThickness" Value="0"></Setter>
<Setter Property="FontFamily" Value="{StaticResource HelveticaNeue}"></Setter>
<Setter Property="FontSize" Value="14"></Setter>
<Setter Property="Foreground" Value="#ff9f00"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Transparent"></Setter>
<Setter Property="Foreground" Value="#ff9f00" />
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Background" Value="Transparent"></Setter>
<Setter Property="Foreground" Value="White" />
</Trigger>
<!--I want to bind current page value in place of 1-->
<DataTrigger Binding="{Binding}" Value="1">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
Комментарии:
1. Поскольку триггер не работает при запуске свойств
Button
,DataTrigger
он не должен быть частью кнопкиControlTemplate
, которая является «внутренним» контекстом управления, независимым от любого типа данных. Поскольку триггер срабатывает при свойствах текущей кнопкиDataContext
,DataTrigger
он должен находиться за пределамиButton
, где он может получить доступ кDataContext
подобному вStyle
таргетингеButton
или, может быть, даже лучше, вDataTemplate
шаблоне элемента. Здесь вы находитесь в контексте, который специализируется на конкретномDataContext
, т.Е. типе данных.2. Вы могли бы сделать так, чтобы listbox, а не itemscontrol, привязывал Foreground к свойству selecteditem . Сделайте PageList списком viewmodels, а не просто строкой. Добавьте к этому свойство кисти. Или используйте конвертер, чтобы преобразовать число в кисть. С запасным значением WordOrangeBush.
Ответ №1:
Решение для преобразования значений
Вы не можете привязать свойство Value
a DataTrigger
к свойству, потому что оно не является свойством зависимости. Вы можете обойти это, создав пользовательский многозначный конвертер, который проверяет страницы на равенство.
public class PageEqualityToBooleanConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return (int)values[0] == System.Convert.ToInt32(values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
Первое значение , передаваемое конвертеру , — это текущая страница as int
, а второе значение — страница на Button
as string
. Немного странно, что ваш current_page
имеет тип int
, но PageList
это набор типов string
, поэтому нам нужно преобразовать второе значение.
Создайте экземпляр конвертера в своих ресурсах перед вашим стилем, чтобы вы могли ссылаться на него.
<local:PageEqualityToBooleanConverter x:Key="PageEqualityToBooleanConverter"/>
Наконец, замените свой DataTrigger
на приведенный ниже. Мы используем a MultiBinding
, чтобы иметь возможность привязывать более одного значения. Конвертер преобразует оба значения в True
, если значения страницы совпадают, в противном False
случае .
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource PageEqualityToBooleanConverter}">
<Binding Path="DataContext.pagination.current_page" RelativeSource="{RelativeSource AncestorType={x:Type ItemsControl}}"/>
<Binding/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
Первая привязка найдет родительский ItemsControl
элемент для доступа к модели представления, которая содержит current_page
. Вторая привязка будет привязана к контексту данных соответствующей кнопки, которая является строкой страницы. Если вы измените PageList
тип элемента на int
это, это все равно будет работать.
Рекомендации
Я хочу указать на некоторые замечания по вашему коду, которые могут помочь вам улучшить его.
- Как я уже упоминал, кажется странным, что тип элемента вашей коллекции отличается от вашего текущего типа свойства элемента. Рассмотрите возможность создания
int
или создания отдельной модели представления элемента страницы, если вы планируете использовать в ней другие свойства, кроме номера страницы. - Вы определяете неявный стиль для
Button
, который применяется ко всемButton
элементам управления в области видимости. Если вы используете его только в своем элементе управления нумерацией страниц, это может быть нормально, но если вы собираетесь использовать его в других местах,DataTrigger
его не следует включать в него, поскольку он специфичен только для этого контекста данных. Создайте отдельный стиль на основе этого. Рассмотрим комментарий @BionicCode по этому поводу. - Как указал @Andy в комментариях, a
ListBox
может лучше подойти для разбиения на страницы, потому что у него есть понятие aSelectedItem
(иIsSelected
свойства его контейнеров элементов, которые могут быть привязаны к aDataTrigger
), что вы и пытаетесь сделать здесь вручную.
Ответ №2:
Value
Свойство a DataTrigger
не может быть привязано к данным.
Что вам нужно сделать, это создать тип, представляющий a Page
, добавить Number
IsCurrentPage
к нему свойство a и и изменить тип PageList
на an ObservableCollection<Page>
вместо an ObservableCollection<string>
.
Затем вы могли бы просто найти соответствующий Page
в PageList
и установить его IsCurrentPage
свойство из модели представления всякий current_page
раз, когда свойство установлено.
Вы также должны убедиться, что получатель PageList
свойства не создает новую коллекцию при каждом ее вызове.