1

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(); } 
3
  • Could just do a quick datatrigger, or a direct binding to say a togglebutton IsChecked property in pure xaml, or a value converter like peter shows below. There's multiple ways to accomplish this. Commented Jun 27, 2017 at 15:07
  • Edited my answer Commented Jun 27, 2017 at 15:08
  • Feel free to upvote both answers mate Commented Jun 27, 2017 at 16:22

3 Answers 3

3

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> 
Sign up to request clarification or add additional context in comments.

Comments

1

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> 

1 Comment

Note that the DataTrigger for Value="False" is redundant, because it only sets the default value of the IsReadOnly property. It's also unclear why you show the other Triggers here. Finally, if you set the Style's TargetType to Button, you wouldn't have to write qualified property names. That said, I would always favor Triggers over value converters. No need to have an extra class.
0

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> 

2 Comments

Peter above has mentioned using a converter, I tend to favor using these to covert business logic values to UI based things like colors or visibility but many people prefer using them for what you're after also.
return !_selectedRecord?.IsNew ?? true; - short and "readable" code ;)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.