WPF - Strange behaviour of TextBox when disabling from view model - c#

I have this xaml code in user control:
<StackPanel Orientation="Horizontal" IsEnabled="{Binding CanEditOpenCut}">
<TextBox Text="{Binding OpenCut}" Margin="5" Width="80"/>
<TextBlock Text="°C" />
</StackPanel>
And I have viewmodel with properties:
private decimal _openCut;
public decimal OpenCut
{
get
{
return _openCut;
}
set
{
if (Set(ref _openCut, value))
{
RaisePropertyChanged(() => OpenCut);
if (this.PrevRunConfig != null)
{
this.PrevRunConfig.CloseCut = _openCut;
}
}
}
}
private bool _canEditOpenCut;
public bool CanEditOpenCut
{
get
{
return _canEditOpenCut;
}
set
{
if (Set(ref _canEditOpenCut, value))
{
}
}
}
Now the binding works fine when I set in textbox e.g. 40 then I have it in my OpenCut property in viewmodel. If I set something in viewmodel I get it in the textbox. So far its working. Now I try to disable the textbox and reset the value to some default like 20. There are 2 scenarios one is working and one not.
1) If I set e.g. 40 in textbox, then lose focus and then set CanEditOpenCut = false and OpenCut = 20, everything works I see 20 in textbox.
2) I set 40 to the textbox, I DO NOT !!! lose focus of the textbox, then I set CanEditOpenCut = false and OpenCut = 20. Now it is not working I still see 40 in textbox although the values in viewmodel are correct.
Thanks for any help,
Tomas

Related

How to use LostFocus in Caliburn Micro

I need to validate the value in the text box when the users finish their input.
When they go to the next text box, the previous text box will validate the range of input (100~100000 steps 100 and format is "###,###"). And my opinion is Lost Focus Event is the good way to solve this.
I have this property which I want to use Lost Focus
private string resultCommandNote02;
public string ResultCommandNote02
{
get
{
return resultCommandNote02;
}
set
{
resultCommandNote02 = value;
NotifyOfPropertyChange(() => ResultCommandNote02);
}
}
And here is the code of View.XAML
<TextBox Grid.Column="1" Margin="0 0 0 4" Text="{Binding ResultCommandNote01}"
cal:Message.Attach="[Event LostFocus] = [Action TxtCmdNote01_LostFocus]"></TextBox>

Xaml How to Enable/Disable and Validate text item in one time

Using MVVM, Devexpress WPF, c#
For example I have two items in a view (I have more in real project) and using them for user to enter some search paramaters and need to validate them and the same time disable another one, if something is written in the first field and overwise.
What I do, I use Validate to determ the Lenght of value and I need to Enable/Disable even if lenght of entered text doesn't meet required lenght
View
Xaml:
<dxe:TextEdit
Text="{Binding SearchField1, UpdateSourceTrigger=PropertyChanged}"
Validate="searchFieldValidate"/>
<dxe:TextEdit
IsEnabled="{Binding IsTextItemEnabled}"
Text="{Binding SearchField2, UpdateSourceTrigger=PropertyChanged}"
Validate="searchFieldValidate"/>
c#
private void searchFieldValidate(object sender, DevExpress.Xpf.Editors.ValidationEventArgs e)
{
if (e.Value == null) return;
if (e.Value.ToString().Length > 5) return;
e.IsValid = false;
e.ErrorType = DevExpress.XtraEditors.DXErrorProvider.ErrorType.Warning;
e.ErrorContent = "Enter more than 5 symbol";
}
ViewModel
Here I set IsTextItemEnabled depending on is value empty or mot
public string SearchField1
{
get { return _searchField1; }
set
{
if (value != _searchField1)
{
_searchField1 = value;
if (!String.IsNullOrEmpty(value))
IsTextItemEnabled = false;
else
IsTextItemEnabled = true;
RaisePropertiesChanged("SearchField1");
}
}
}
A problem is that RaisePropertiesChanged doesn't work until field lenght doesn't reach 5 symbols
Could You please help me to solve this problem? First of all I am trying to Disable one field, so I use one bool IsTextEnabled.. what about oposite variant...
I gave names to TextEdit
<dxe:TextEdit
Name="txtEdit1"
Text="{Binding SearchField1, UpdateSourceTrigger=PropertyChanged}"
Validate="searchFieldValidate"/>
<dxe:TextEdit
Name="txtEdit2"
Text="{Binding SearchField2, UpdateSourceTrigger=PropertyChanged}" />
and add a line to validate procedure:
txtEdit2.IsEnabled = String.IsNullOrEmpty(e.Value?.ToString());
So now I am able to validate and enable/disable at one time.. Don't use OnPropertiesChanged this time

Two-way-binding: editing passed value from XAML control in the model setter does not update control

This is for a Windows 10 Universal App.
XAML:
<RelativePanel Padding="4" Margin="4,12,0,0">
<TextBlock x:Name="Label" Text="Class Name" Margin="12,0,0,4"/>
<ListView x:Name="ClassTextBoxes"
ItemsSource="{Binding TextBoxList}"
SelectionMode="None" RelativePanel.Below="Label">
<ListView.ItemTemplate>
<DataTemplate >
<RelativePanel>
<TextBox x:Name="tbox"
PlaceholderText="{Binding PlaceHolder}"
Text="{Binding BoxText,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Padding="4" Width="200" MaxLength="25"/>
<TextBlock x:Name="errorLabel"
RelativePanel.Below="tbox"
Text="{Binding Error, Mode=TwoWay}"
Padding="0,0,0,4"
FontSize="10"
Foreground="Red"/>
<Button Content="Delete" Margin="12,0,0,0" RelativePanel.RightOf="tbox"/>
</RelativePanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</RelativePanel>
Model:
public class TextBoxStrings : BaseModel
{
private string _placeholder;
public string PlaceHolder
{
get { return _placeholder; }
set
{
if (_placeholder != value)
{
_placeholder = value;
NotifyPropertyChanged();
}
}
}
private string _boxText;
public string BoxText
{
get { return _boxText; }
set
{
if (_boxText != value)
{
_boxText = CheckBoxText(value);
NotifyPropertyChanged();
}
}
}
public string CheckBoxText(string val)
{
var r = new Regex("[^a-zA-Z0-9]+");
return r.Replace(val, "");
}
}
ViewModel:
private TrulyObservableCollection<TextBoxStrings> _textBoxList;
public TrulyObservableCollection<TextBoxStrings> TextBoxList
{
get { return _textBoxList; }
set
{
if (_textBoxList != value)
{
_textBoxList = value;
RaisePropertyChanged();
}
}
}
and I add new TextBoxString objects to my TextBoxList collection from within my view-model.
I want to make it that users can't type in certain characters (or rather, they get deleted whenever they
are typed in.
This works...in the model. Setting breakpoints and looking at the values, everything in the Model is working: value goes into the setter and gets changed, _boxText holds the new value that is set from CheckBoxText();
But the problem is, in my View, the textbox doesn't reflect changes to the underlying text that I make in the model.
So if I type in "abc*()" into "tbox", the value in the model will be "abc". The value of the textbox, however, will still be "abc*()".
I have a feeling it has something to do with the fact that I'm editing items that are inside of a collection and I don't have anything implemented to handle changing items within a collection. I was under the impression that using INotifyPropertyChanged and ObservableCollection<T> would take care of that for me.
Does anyone have any suggestions?
Thank you!
Edit: So, now I'm trying to use TrulyObservableCollection because I thought this was the problem, but it hasn't helped. Here it is: https://gist.github.com/itajaja/7507120
But the problem is, in my View, the textbox doesn't reflect changes to the underlying text that I make in the model.
As you've seen, the TextBox do reflect changes to your model. When you type in "abc*()" in the TextBox, the value in the model will be changed to "abc". The problem here is that the binding system in UWP is "intelligent". For TwoWay bindings, changes to the target will automatically propagate to the source and in this scenario, binding system assumes that the PropertyChanged event will fire for corresponding property in source and it ignores these events. So even you have RaisePropertyChanged or NotifyPropertyChanged in you source, the TextBox still won't update.
In WPF, we can call BindingExpression.UpdateTarget Method to force the update. But this method is not available in UWP.
As a workaround, you should be able to use TextBox.TextChanged event to check the input like following:
private void tbox_TextChanged(object sender, TextChangedEventArgs e)
{
var tb = sender as TextBox;
if (tb != null)
{
var originalText = tb.Text;
var r = new Regex("[^a-zA-Z0-9]+");
if (originalText != r.Replace(originalText, ""))
{
var index = (tb.SelectionStart - 1) < 0 ? 0 : (tb.SelectionStart - 1);
tb.Text = r.Replace(originalText, "");
tb.SelectionStart = index;
}
}
}
However it may break your MVVM model, you can use data validation to avoid this and here is a blog: Let’s Code! Handling validation in your Windows Store app (WinRT-XAML) you can refer to. And for my personal opinion, data validation is a better direction for this scenario.
if (_boxText != value)
{
_boxText = CheckBoxText(value);
NotifyPropertyChanged();
}
Try changing this to:
var tmp = CheckBoxText(value);
if (_boxText != tmp)
{
_boxText = tmp;
NotifyPropertyChanged();
}
I hope, in your XAML, the binding to property BoxText is two-way, right?
You should edit BoxText and then send checked value to UI. Just send value to CheckBoxText and already edited should be assigned to _boxText. And then you should send BoxText to UI by calling RaisePropertyChanged("BoxTest"). Please, see the following code snippet:
private string _boxText;
public string BoxText
{
get { return _boxText; }
set
{
if (_boxText != value)
{
_boxText=CheckBoxText(value);
RaisePropertyChanged("BoxText");
}
}
}
There is no difference where you use INotifyPropertyChanged for one property of for properties placed in collection. The complete example with collections and ListView can be seen here

Failing To Bind Multiple UI Components in WPF

Alright I tried my best but looks like I need help. I have a textbox, a listview and a button in my xaml file. Listview has two columns: Devicename and DeviceAddress. I have done a binding of both the listview and textbox in such a way, that whenever I select an item in listview(I2CDeviceList), the deviceaddress(2nd Column) gets displayed in my textbox.
XAML:
<TextBox PreviewTextInput="AddressBox_PreviewTextInput" Name="AddressI2C" Text="{Binding SelectedItem.I2CDeviceAddress, Path=AddressMessage, Mode=TwoWay, ElementName=I2cDeviceList}" />
<Button Content="I2C Read" Command="{Binding Path=I2CReadCommand}" Name="button9" />
<ListView Grid.Column="0" ItemsSource="{Binding I2CDeviceList}" SelectedItem="{Binding SelectedI2CDeviceList, Mode=TwoWay}" Height="100" HorizontalAlignment="Stretch" Name="I2cDeviceList" VerticalAlignment="Stretch" Width="Auto" >
<ListView.View>
<GridView>
<GridViewColumn Header="I2C Device" Width="Auto" DisplayMemberBinding="{Binding I2CDevName}" />
<GridViewColumn Header="I2C Device Address" Width="Auto" DisplayMemberBinding="{Binding I2CDeviceAddress}" />
</GridView>
</ListView.View>
</ListView>
Thus using SelectedItem.I2CDeviceAddress gives me the deviceaddress in my Textbox.
Now my view model has a property for the Button and the textbox and has the following method which gets invoked when button is clicked:
public void I2CReadCommandExecuted()
{
ReadMessage = string.Empty;
Byte[] buffer = new Byte[512];
int address;
string strValue = AddressMessage;
if (strValue.StartsWith("0x"))
{
strValue = strValue.Remove(0, 2);
address = Convert.ToInt32(strValue);
mComm.setAddress(address);
}
}
// This is for textBox
private string _AddressMessage = string.Empty;
public string AddressMessage
{
get
{
return _AddressMessage;
}
set
{
_AddressMessage = value;
NotifyPropertyChanged("AddressMessage");
}
}
// Property for ListView
public ObservableCollection<I2CModel> I2CDeviceList
{
get { return _I2CDeviceList; }
set
{
_I2CDeviceList = value;
NotifyPropertyChanged("I2CDeviceList");
}
}
// Property for Selected Item in ListView
private I2CModel _selectedI2CDeviceList;
public I2CModel SelectedI2CDeviceList
{
get { return _selectedI2CDeviceList; }
set
{
_selectedI2CDeviceList = value;
NotifyPropertyChanged("SelectedI2CDevSize");
}
}
Basically I have to remove the 0x from the value and store the hexadecimal value in my integer variable.
Here I am facing two issues:
When I put both Text="{Binding SelectedItem.I2CDeviceAddress, Path=AddressMessage, Mode=TwoWay, ElementName=I2cDeviceList}" the seelcted address from the listview doesnt appear in my textbox. The moment I remove Path=AddressMessage, Mode=TwoWay,, it works fine. How to make sure both of them work smoothly? Is their any other way I can get the selected item from the listview and display it in my textbox?
By using string strValue = AddressMessage; I am trying to save the content of AddressMessage in the string but when I debug my code, it always shows "null" even though I have "0x23"(hardcoded) in my textbox. Due to this I get the following error: Object reference not set to an instance of an object. at the beginning of if condition.
I tried my level best but it ain't happening. Am i missing something?
First of all there is no need to have seperate AddressMessage property. It can be done using SelectedI2CDeviceList. But still if you want to use it it can be achieved through below changes -
Set AddressMessage property when the selected item of listview changes
public I2CModel SelectedI2CDeviceList
{
get { return _selectedI2CDeviceList; }
set
{
_selectedI2CDeviceList = value;
AddressMessage = _selectedI2CDeviceList.I2CDeviceAddress;
NotifyPropertyChanged("SelectedI2CDevSize");
}
}
Also change the binding of textbox to below one:
<TextBox
Name="AddressI2C"
Text="{Binding Path=AddressMessage, Mode=TwoWay}" />
Hence whenever selected item of the listview changes it will set the content for textbox and when AddressMessage property is properly set you want get your second issue.
Hope this helps.

Conditional textboxes with slider binding

I have three texboxes and one slider which changes their Text properties. What i have to do is to bind slider's value property with Text textbox property but in a specific way. When one of textboxes are activated(gotfocused) i need slider to change its Text property. And only that one. I have binded it so far but when i move the slider all textboxes are updated.
Any ideas?
I was reading about converters, but i don't see how to implement it within my program.
http://forums.create.msdn.com/forums/t/95548.aspx here you have got code of my slider and textblock.
What about simply changing the active binding when a textbox receives focus:
Code Behind:
private Binding _activeBinding;
private TextBox _activeTextbox;
private TextBox ActiveTextBox
{
get { return _activeTextbox; }
set
{
// Check if a binding exists, initialize if one does not
if (_activeBinding == null)
{
_activeBinding = new Binding("Value");
_activeBinding.Source = this.sld;
}
if (_activeTextbox != null)
{
// Clear the binding
_activeTextbox.ClearValue(TextBox.TextProperty);
}
_activeTextbox = value;
if (_activeTextbox != null)
{
// Set the new binding
_activeTextbox.SetBinding(TextBox.TextProperty, _activeBinding);
}
}
}
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
this.ActiveTextBox = sender as TextBox;
}
XAML:
<Grid>
<StackPanel>
<TextBox GotFocus="TextBox_GotFocus">1</TextBox>
<TextBox GotFocus="TextBox_GotFocus">2</TextBox>
<TextBox GotFocus="TextBox_GotFocus">3</TextBox>
<Slider x:Name="sld"></Slider>
</StackPanel>
</Grid>

Categories

Resources