Unable to disable controls(TextBoxes) using MVVM in WPF? - wpf

I Have a text box, depending on the text box value i need to enable/disable other text Boxes.I am using MVVM Pattern.
So here's my problem , whenever i enter some text in TextBox1 ,the Setter for TextBox1 is getting fired and i am able to check in if loop ,whether valus exists and i am disabling other text boxes. Now, when there is single value in text box say "9" and i am deleting / Backspacing it the Set event is not getting triggered in order to enable the other Text Boxes.
View:
<TextBox Text = {Binding TextBox1 , UpdateSourceTrigger = PropertyChanged,Mode= TwoWay}/>
<TextBox Text = {Binding TextBox2 , UpdateSourceTrigger = PropertyChanged,Mode= TwoWay}/>
<TextBox Text = {Binding TextBox3 , UpdateSourceTrigger = PropertyChanged,Mode= TwoWay}/>
View Model:
private int_textBox1;
public int TextBox1
{
get {return _textBox1;}
set
{
_textBox1= value;
if(value > 0)
{
//Code for Disabling Other Text Boxes (TextBox2 and TextBox3)
}
else
{
// Code for Enabling Other Text Boxes (TextBox2 and TextBox3)
}
NotifyPropertyChanged("TextBox1");
}
}

If you are using MVVM pattern, you should create boolean properties, and bind TextBox.IsEnabled property to it. Your boolean properties should raise PropertyChanged event, in order to tell the view (TextBox in your case) that your property was really changed:
public bool IsEnabled1
{
get { return _isEnabled1; }
set
{
if (_isEnabled1 == value)
{
return;
}
_isEnabled1 = value;
RaisePropertyChanged("IsEnabled1");
}
}
and then in xaml:
<TextBox Text="{Binding TextBox1, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
IsEnabled="{Binding IsEnabled1}" />
and so on with other TextBoxes

first of all, if you set your updatesourcetrigger to Propertychanged - your setter is called when you do anything in your textbox. i check this in a simple testproject. btw do you call OnPropertyChanged in your setter, because its not in your sample code?
if not your binding seems to be broken. so pls check your binding or post some more relevant code.
EDIT:
if you change your type to int? you can do the following xaml
<TextBox Text="{Binding MyNullableInt, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, TargetNullValue=''}"/>

Related

Wpf clear disabled TextBox.Text when there is validation error

I have three textboxes with text binded to three properties.
I need to disable two textboxes, when i type in the third one. And i have to clear the value of the disabled textboxes.
`
<TextBox Text="{Binding TextProperty1}" IsEnabled="{Binding T1Enabled}"/>
<TextBox Text="{Binding TextProperty2}" IsEnabled="{Binding T2Enabled}"/>
<TextBox Text="{Binding TextProperty3}" IsEnabled="{Binding T3Enabled}"/>
`
T1-3Enabled is a property with only getters, and i raise propertychanged on textboxes' lost focus command. When these properties refreshed i clear the binded propertes of the disabled textboxes (TextProperty1-3).
But, when some of the disabled textboxes have validation errors, the source property is cleared, but the textbox.text is not.
How can i solve this in mvvm? I dont want to set textbox.text.
I hope the problem is clear.
Thanks for any help or other solution.
I solved the problem with a derived textbox class.
public class MyTextBox : TextBox
{
public MyTextBox()
{
IsEnabledChanged += MyTextBox_IsEnabledChanged;
}
private void MyTextBox_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if(e.NewValue is bool)
if (!(bool)e.NewValue)
Text = string.Empty;
}
}

WPF MVVM getting the textbox data to the ViewModel

I have read many questions on this and so far I've not been able to find the answer on this apparently simple issue.
I have a view model, in which is a property. In my XAML I have a TextBox with a binding to that property.
But the property never seems to change.
Here's the textbox:
<TextBox Grid.Row="1"
Grid.Column="0"
Margin="4"
Text="{Binding CharNameFromTB}" />
And the relevant code behind for the ViewModel:
private String _charNameFromTB;
String CharNameFromTB
{
get { return _charNameFromTB; }
set
{
if (!string.Equals(this._charNameFromTB, value))
{
this._charNameFromTB = value;
RaisePropertyChanged("CharNameFromTB");
}
}
}
I have put a break point on the if statement in the setter, but it never triggers. Have I missed something obvious out? I tried setting the binding mode to twoway but that didn't change anything.
It's driving me a little mad. Any help would be appreciated!
You should make the property public in order to be able to bind to it:
private String _charNameFromTB;
public String CharNameFromTB
{
get { return _charNameFromTB; }
set
{
this._charNameFromTB = value;
RaisePropertyChanged("CharNameFromTB");
}
}
Also make sure that you have set the DataContext of the TextBox or any of its parent elements to an instance of your view model class where the CharNameFromTB property is defined.
Also note that by default, the source property is set when the TextBox loses focus.
If you want to update the source property on each keystroke you should set the UpdateSourceTrigger property of the Binding to PropertyChanged:
<TextBox Grid.Row="1"
Grid.Column="0"
Margin="4"
Text="{Binding CharNameFromTB, UpdateSourceTrigger=PropertyChanged}" />

How to bind checkbox, combobox and radiobutton to settings value in ".setting" file in wpf?

How to bind checkbox, combobox and radiobutton to their respective settings value in ".setting" file in wpf?
I know how to bind textblock to value in settings file. Here is the code
<TextBox Margin="5,38,5,2" Width="100" Height="50" Text="{Binding Source={StaticResource Settings}, Path=Default.Name, Mode=TwoWay}" />
a more generic, decoupled solution would be to have wrapping properties in your ViewModel and bind it to them instead directly to your .settings file.
<CheckBox IsChecked="{Binding IsChecked}"/>
And in the ViewModel
public bool IsChecked
{
get
{
return Settings.Default.IsCheckedVal;
}
set
{
Settings.Default.IsCheckedVal = value;
RaisePropertyChanged(() => IsChecked);
}
}
Do the same for ComboBox and RadioButton values.
If you need to bind a ComboBox or the RadioButton to an enum or other types then what they are expecting, you can use converters
You will also have to use
Settings.Default.Save(); to save your settings either in a general dedicated command or in every property setter - whatever suits your logic best.

databinding and focus coordination

I have several controls including a DataGrid that I want to be disabled until there is a valid value in the first TextBox in the presentation. So I added a boolean property to bind to in the VM and bind to it in the xaml (below).
The binding works, but has the side effect of 'trapping' the user in the TextBox (MoneyToAllocate).
Presumably this is because the TB binding is LostFocus and there is no place for the focus to go and actually trigger the updates. What's a good way to fix this?
Cheers,
Berryl
ViewModel
public bool HasMoneyToAllocate { get { return MoneyToAllocate.Amount > 0; } }
public Money MoneyToAllocate {
get { return _moneyToAllocate; }
set {
if (value.Amount < 0) return;
_moneyToAllocate = new Money(value.Amount, SelectedCurrency);
NotifyPropertyChanged(() => HasMoneyToAllocate);
}
}
View
<TextBox Text="{Binding MoneyToAllocate, Converter={StaticResource moneyConverter}}" />
<DataGrid IsEnabled="{Binding HasMoneyToAllocate}" ...
EDIT
I should have added that I tried PropertyChanged for update but it gets a bit messy since the value of the text box needs to be formatted by the converter. Any other ideas?
FINAL EDIT
I wound up letting another control that previously wasn't a tab stop be a tab stop, so the text box had a place to go. Phil understood the problem best and gets the answer, even though the range of values the user can input (.001 to decimal.MaxValue) make an up-down impractical.
Use UpdateSourceTrigger=PropertyChanged
<TextBox
Text="{Binding MoneyToAllocate, UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource moneyConverter}}" />
Then you have to use UpdateSourceTrigger=PropertyChanged
- if you use that binding you are using the value in the VM will not effected till the focus moves from the textBox
- but if you add UpdateSourceTrigger=PropertyChanged to your binding the VM property (MoneyToAllocate) will effected immediately (when the textBox.Text value changed)

XAML ReadOnly ComboBox

To set up a ReadOnly ComboBox in XAML (WPF), you have to set up a ComboBox and a TextBox showing only one of them according to a pair of properties IsReadOnly/IsEditable that must exist on your ViewModel. Note that on this sample "UserNVL" must exist in the resources and it should be a NameValueList collection that allows us to convert ID to names. In this case the RecipientID is the key for a user name. Note also the VisibilityConverter must also exist in the resources and it's a standard BooleanToVisibilityConverter.
Gosh! This was so hard to find I had to made it myself. This allows the user the select the content of the text box. No way a disabled ComboBox would ever allow you to do it.
There are two properties named IsHitTestVisible & IsTabVisible. the former makes the control deaf to mouse events and the latter to keyboard events.
This could help you as it would not give the disabled look to your combo box but you will succeed in making a read only combo box..
Source :-
http://www.telerik.com/community/forums/wpf/combobox/isreadonly-does-seem-to-work.aspx
Why not just set IsEnabled=false?
<DockPanel>
<TextBlock Text="Recipient" Margin="6,9,3,6" HorizontalAlignment="Right"/>
<ComboBox
x:Name="RecipientID"
ItemsSource="{Binding Source={StaticResource UserNVL}}"
DisplayMemberPath="Value"
SelectedValuePath="Key"
SelectedValue="{Binding Path=RecipientID}"
Height="20"
Margin="6,6,0,6"
MinWidth="200"
HorizontalAlignment="Left"
IsEditable ="True"
Visibility="{Binding Path=IsEditable, Converter={StaticResource VisibilityConverter}}"/>
<TextBox
x:Name="RecipientName"
Text="{Binding ElementName=RecipientID, Path=Text}"
Margin="6,6,0,6"
MinWidth="200"
HorizontalAlignment="Left"
Style="{StaticResource textBoxInError}"
Visibility="{Binding Path=IsReadOnly, Converter={StaticResource VisibilityConverter}}"/>
</DockPanel>
I think that you will find it much easier and practical to create a class to extend the ComboBox class in this very simple manner:
override the OnSelectionChanged method of the Combobox to check the property IsReadOnly before to allow base.OnSelectionChanged(e) to run.
That way you just have to set ComboBox.IsReadOnly property to True. No big XAML to write everywhere...
Here is a custom ComboBox subclass that gives the read only behaviour I needed for my scenario:
public class ReadOnlyComboBox : ComboBox
{
static ReadOnlyComboBox()
{
IsDropDownOpenProperty.OverrideMetadata(typeof(ReadOnlyComboBox), new FrameworkPropertyMetadata(
propertyChangedCallback: delegate { },
coerceValueCallback: (d, value) =>
{
if (((ReadOnlyComboBox)d).IsReadOnly)
{
// Prohibit opening the drop down when read only.
return false;
}
return value;
}));
IsReadOnlyProperty.OverrideMetadata(typeof(ReadOnlyComboBox), new FrameworkPropertyMetadata(
propertyChangedCallback: (d, e) =>
{
// When setting "read only" to false, close the drop down.
if (e.NewValue is true)
{
((ReadOnlyComboBox)d).IsDropDownOpen = false;
}
}));
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
if (IsReadOnly)
{
// Disallow changing the selection when read only.
e.Handled = true;
return;
}
base.OnSelectionChanged(e);
}
}
Points about this approach:
Doesn't break any existing styles applied to the element, unlike an approach that introduces additional UI elements.
Doesn't break input focus while read only. You can still tab into and click to focus this element. This is more accessible, which is a concern in my scenario.
The UI element doesn't, but default, look any different when read only. If you need that, you would have to apply relevant styles to make it so.
If IsEnabled is set to false, Combobox value is nearly not readable. What I found as suitable solution is:
combobox and textbox (formated as readonly) are in the same grid position
combobox spans to next column to gain additional 15 width so dropdown button is visible
textbox.IsVisible is bound to combobox.IsEnabled with bool to visibility converter.
textbox.Text is bound to combobox.SelectedItem (in my case it is strongly typed so I actually bound into .DisplayText of it)

Resources