Проблема привязки пользовательского элемента управления 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

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

Другие вопросы по тегам