user1901820
user1901820

Reputation: 51

XAML Textbox readonly if a bool value is false

I have a textbox in my xaml for an ID field that is pulled from a datasource. I want this value to be readonly so that users can't change this value. I have a button for the user to add a new object which of course will require an ID. I have a bool property on my object "IsNew" that gets set to true when the user clicks the "Add New" button. When that button is clicked, I want this textbox to be editable. So basically, when "IsNew = true". How can I accomplish this?

//My Xaml for the textbox:
 <TextBox x:Name="ID"
                 Text="{Binding SelectedRecord.ID}"/>

//Xaml for the button
<Button x:Name="AddNewRecordButton"
              Grid.Column="1"
              Margin="20,0,5,0"
              HorizontalAlignment="Left"
              VerticalAlignment="Bottom"
              HorizontalContentAlignment="Left"
              Height="24"
              Width="90"
              Command="{Binding AddNewRecordCommand}"
              CommandParameter="{Binding ElementName=window}"/>

//Code for the command method
 public void AddNewRecord(object parameter)
{
  var newRecord = new StockingReason();
  Records.Add(newRecord);
  SelectedRecord = newRecord;
  newRecord.IsNew = true;
  var control = parameter as IFocusable;
  control?.SetFocus();
}

Upvotes: 1

Views: 2340

Answers (3)

Peter
Peter

Reputation: 1687

Use an IValueConverter:

public class InvertBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool val = (bool)value;
        return !val;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool val = (bool)value;
        return !val;
    }
}

Example how use it

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:metro="http://metro.mahapps.com/winfx/xaml/controls"
        xmlns:local="clr-namespace:WpfApp1"
        xmlns:converter="clr-namespace:WpfApp1.MYCONVERTERNAMESPACE"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <converter:InvertBoolConverter x:Key="InvertBoolConverter" />
    </Window.Resources>

    <TextBox x:Name="ID"
             Text="{Binding SelectedRecord.ID}"
             IsReadOnly="{Binding IsNew, Converter={StaticResource InvertBoolConverter}}"/>

</Window>

Upvotes: 3

James
James

Reputation: 15

So I think if I understand your requirement, you can do the following:

1) Make sure the class that your code lives in implements INotifyPropertyChanged

2) Create a new Boolean property on your class called IsRecordReadOnly and have it forwarding the inverse of the selected records IsNew Flag.

3) Call propertychanged on your new boolean property after you've updated your selected selected record.

4) Bind the IsReadOnly property on the textbox to the IsRecordReadOnly property in the ViewModel.

public class ViewModel : INotifyPropertyChanged
    {
    private IRecord _selectedRecord;

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public List<IRecord> Records  = new List<IRecord>();

    public IRecord SelectedRecord
    {
        get { return _selectedRecord; }
        set
        {
            _selectedRecord = value;
            OnPropertyChanged();
        }
    }

    public bool IsRecordIdReadOnly
    {
        get { return !_selectedRecord?.IsNew ?? true; }
    }

    public void AddNewRecord(object parameter)
    {
        var newRecord = new StockingReason();
        Records.Add(newRecord);
        SelectedRecord = newRecord;
        newRecord.IsNew = true;
        var control = parameter as IFocusable;
        control?.SetFocus();
    }
}

and then your xaml...

<Grid>
    <TextBox x:Name="ID"
             Text="{Binding SelectedRecord.ID}" IsReadOnly="{Binding IsRecordReadOnly}"/>
</Grid>

Upvotes: 0

Tony
Tony

Reputation: 17637

You can use a IValueConverter, as pointed by Peter,
or you can also use a WPF Trigger

See WPF Trigger binding to MVVM property

<TextBox>
        <TextBox.Style>
            <Style>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsNew}" Value="True">
                        <Setter Property="TextBox.IsReadOnly" Value="False" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding IsNew}" Value="False">
                        <Setter Property="TextBox.IsReadOnly" Value="True" />
                    </DataTrigger>

                    <Trigger Property="Control.IsMouseOver" Value="true">
                        <Setter Property="Control.FontStyle" Value="Italic"></Setter>
                        <Setter Property="Control.Foreground" Value="Red"></Setter>
                        <Setter Property="Control.Background" Value="Yellow"></Setter>
                    </Trigger>
                    <Trigger Property="Button.IsPressed" Value="true">
                        <Setter Property="Control.Foreground" Value="Green"></Setter>
                        <Setter Property="Control.Background" Value="Blue"></Setter>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>

Upvotes: 1

Related Questions