Проблема привязки пользовательского элемента управления WPF
Это должен быть очень простой случай, но я дергаю волосы, пытаясь заставить их работать. Вот установка:
Я разрабатываю приложение, которое будет иметь режим только для чтения и режим редактирования для некоторых данных. Поэтому я создал пользовательский элемент управления, который представляет собой текстовое поле и текстовый блок, привязанные к одним и тем же текстовым данным и условно видимые, основываясь на свойстве EditableMode (поэтому, когда оно редактируемое, отображается текстовое поле, а когда оно не отображается).
Теперь я хочу, чтобы многие из этих элементов управления были в моем главном окне, и чтобы они были связаны с одним свойством bool. Когда это свойство изменяется с помощью кнопки, я хочу, чтобы все TextBlocks превратились в TextBoxes или обратно.
Моя проблема заключается в том, что элемент управления правильно установлен на привязку, и если я делаю myUserControl.Editable = true. Но это не изменится, если привязать его к свойству bool.
Вот код для моего пользовательского контроля:
<UserControl x:Class="CustomerCareTool.Controls.EditableLabelControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:CustomerCareTool.Converters"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<UserControl.Resources>
<src:BoolToVisibility x:Key="boolToVisibility" Inverted="False" />
<src:BoolToVisibility x:Key="invertedBoolToVisibility" Inverted="True" />
</UserControl.Resources>
<Grid>
<TextBlock Name="textBlock" Text="{Binding Path=TextBoxValue}" Visibility="{Binding Path=EditableMode, Converter={StaticResource invertedBoolToVisibility}}"/>
<TextBox Name="textBox" Visibility="{Binding Path=EditableMode, Converter={StaticResource boolToVisibility}}">
<TextBox.Text>
<Binding Path="TextBoxValue" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</Grid>
Я использовал конвертер для преобразования bool в видимость и обратный bool в видимость. Не уверен, что это вообще нужно здесь.
И это код позади:
public partial class EditableLabelControl : UserControl
{
public EditableLabelControl()
{
InitializeComponent();
}
public string TextBoxValue
{
get { return (string)GetValue(TextBoxValueProperty); }
set { SetValue(TextBoxValueProperty, value); }
}
public static readonly DependencyProperty TextBoxValueProperty =
DependencyProperty.Register("TextBoxValue", typeof(string), typeof(EditableLabelControl), new UIPropertyMetadata());
public bool EditableMode
{
get { return (bool)GetValue(EditableModeProperty); }
set { SetValue(EditableModeProperty, value); }
}
public static readonly DependencyProperty EditableModeProperty =
DependencyProperty.Register("EditableMode", typeof(bool),typeof(EditableLabelControl), new UIPropertyMetadata(false, EditableModePropertyCallBack));
static void EditableModePropertyCallBack(DependencyObject property,
DependencyPropertyChangedEventArgs args)
{
var editableLabelControl = (EditableLabelControl)property;
var editMode = (bool)args.NewValue;
if (editMode)
{
editableLabelControl.textBox.Visibility = Visibility.Visible;
editableLabelControl.textBlock.Visibility = Visibility.Collapsed;
}
else
{
editableLabelControl.textBox.Visibility = Visibility.Collapsed;
editableLabelControl.textBlock.Visibility = Visibility.Visible;
}
}
}
Теперь в моем основном приложении у меня есть элемент управления, добавленный так:
<Controls:EditableLabelControl x:Name="testCtrl" EditableMode="{Binding Path=Editable}" TextBoxValue="John Smith" Grid.Row="0"/>
Для этого же приложения DataContext имеет значение self
DataContext="{Binding RelativeSource={RelativeSource Self}}"
И код позади выглядит так:
public partial class OrderInfoView : Window, INotifyPropertyChanged
{
public OrderInfoView()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Editable = !Editable;
}
private bool _editable = false;
public bool Editable
{
get
{
return _editable;
}
set
{
_editable = value;
OnPropertyChanged("Editable");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged == null) return;
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Нажатие на кнопку ничего не делает:(Я попробовал все, чтобы заставить это работать, и не играли в кости. Буду очень признателен за помощь!
Я попробовал следующее, и все еще не работает:
public bool Editable
{
get { return (bool)GetValue(EditableProperty); }
set { SetValue(EditableProperty, value); }
}
public static readonly DependencyProperty EditableProperty =
DependencyProperty.Register("Editable", typeof(bool), typeof(OrderInfoView), new UIPropertyMetadata(false));
3 ответа
Похоже, ваше решение может быть более сложным, чем необходимо. Если все, что вы хотите сделать, это отключить TextBox, похожий на TextBlock, то вы можете сделать это, используя триггер и шаблон. Затем вы можете применить этот стиль ко всем текстовым полям.
Вот пример такого подхода:
<Window x:Class="WpfApplication25.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1"
Height="300"
Width="300"
>
<Window.Resources>
<!-- Disable TextBox Style -->
<Style x:Key="_DisableTextBoxStyle" TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<!--
Be sure to apply all necessary TemplateBindings between
the TextBox and TextBlock template.
-->
<TextBlock Text="{TemplateBinding Text}"
FontFamily="{TemplateBinding FontFamily}"
/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<TextBox IsEnabled="{Binding IsChecked, ElementName=uiIsEnabled}"
Style="{StaticResource _DisableTextBoxStyle}"
/>
<ToggleButton x:Name="uiIsEnabled" Content="Enable" IsChecked="True" />
</StackPanel>
</Window>
INotifyPropertyChanged не работает для классов, производных от DependencyObject.
Редактируемое свойство в OrderInfoView должно быть свойством зависимости, чтобы привязка работала правильно, хотя технически ваш код корректен, но я чувствую его ошибку в WPF, что когда объект является объектом зависимости, он игнорирует событие INotifyPropertyChanged, так как он ищет уведомление в системе свойств.
<Controls:EditableLabelControl x:Name="testCtrl"
EditableMode="{Binding Path=Editable,ElementName=userControl}" TextBoxValue="John Smith" Grid.Row="0"/>
Укажите ElementName в теге привязки, а также назовите свой usercontrol с помощью x:FieldName или x:Name.
Я просто наткнулся на это в поисках чего-то другого.
Без подробного прочтения вашего поста (нет времени, извините) мне кажется, что вы столкнулись с проблемой, аналогичной той, о которой я писал здесь: http://jonsblogat.blogspot.com/2009/11/wpf-windowdatacontext-and.html
Короче говоря, перенесите привязку для основного окна в таблицу и используйте относительную привязку, чтобы посмотреть, решит ли это вашу проблему.