Combobox UI resetting itself after internal update during selection - c#

I have a combobox bound to an Enum. When I click on a new value in my combobox, I'm generating a message to an attached device and getting a response back. I want to file whatever value is received back.
My regular-old bound property looks like this:
private Enum enumValue;
public Enum EnumValue
{
get => enumValue;
set
{
if (enumValue != value)
{
enumValue = value;
sendToDevice();
}
}
}
My update from device looks like this:
public void SetValueFromDevice(string valueFromDevice)
{
enumValue = (Enum)Enum.Parse(EnumType, valueFromDevice);
RaisePropertyChanged(nameof(EnumValue));
}
Note that I'm setting the private variable enumValue directly because I don't want to trigger another outgoing message to my device if I don't need to.
Here's the situation: Let's say I have an enum that looks like this:
public enum Sources
{
Off,
Low,
Mid,
Hi
}
The firmware I've written doesn't like the value of low on certain versions on the device. Instead, it'll reply back with whatever the current value is. I want to file that back to the combo box instead of showing the incorrect value to the user.
Now, what seems to happen is after I click on the combobox item, it sends a message to my device, gets the response, and then files it back appropriately. I can step through and see the combobox doing the "get" after I call RaisePropertyChanged, but it doesn't seem to be updating the selected item at all.
I can call SetValueFromDevice and that always works, so it seems like it's the UI acting up.
I even attempted to totally force it with
control.DropDownClosed += new EventHandler(SourceBox_DropDownClosed);
....
private void SourceBox_DropDownClosed(object sender, EventArgs e)
{
ComboBox box = sender as ComboBox;
box.GetBindingExpression(ComboBox.SelectedItemProperty).UpdateTarget();
}
...and that did nothing.
What am I doing wrong here? Is there any way to set the item of the combobox during the item selection interaction?

Figured it out: I needed to make my sendToDevice() call asyncronous, because I'd be triggering the RaisePropertyChanged twice in the same process, and it'd only "see" the first one.
App.Current.Dispatcher.InvokeAsync(() => sendToDevice());

Related

Text Box values chabged but the binding see it as null [duplicate]

I am working on some data collection forms in WinForms/C#. When the form loads, I am looping through a configuration and adding a new Binding to each of the TextBox controls; mapping the Text property of each TextBox control to specific string property on my POCO object.
public void BindTextBoxControls(dynamic entity, List<TextBoxConfig> textBoxConfig)
{
foreach (var config in textBoxConfig)
config.Control.DataBindings.Add(new Binding("Text", entity, config.PropertyName));
}
Everything has been working as expected, new records properly saving new values entered into the corresponding TextBox controls, TextBoxes populating with the correct values when reopened a previously entered records with the form, and updates to values in TextBoxes of previously entered records are getting the updated values set on the underlying POCO.
However, I started to layer in some business rules onto the form specifically to gray out/disable and clear out previously entered values in the TextBox based on other user input/activity on the form - things are not working as expected.
In a contrived example; a rule like if a Checkbox_1 is checked then TextBox #5 should not be valued (clear out any previously entered value and disable it from input). On my Checkbox_1 event handler for CheckedChanged, I specifically check if the Checkbox_1 is checked and if so, set TextBox_1.Text == null and TextBox_1.Enabled = false. This works as expected and on the form, I see any previously entered value cleared from the TextBox_1 and it becomes enabled.
private void chkCheckBox1_CheckedChanged(object sender, EventArgs e)
{
if(!chkCheckBox1.Checked)
{
txtBox5.Text = string.Empty;
}
}
However, when I debug and break on the save and inspect the underlying POCO's property that the underlying control is bound to after the method is called; the old value still persists on the object's property which the text box is bound to, despite the textbox having not value appearing on the form. When I reopen the form for that record, the old cleared out value is re-populated in the disabled TextBox. However, manually clearing out the value in the same TextBox or updating a value and inspecting the object shows the updated value after those operations are performed.
It seems like changing the Text value of a TextBox control (e.g. the Text property of a TextBox) in code maybe somehow be "bypassing" the DataBinding? I'm actually seeing the same/similar behavior when applying similar rules to "uncheck" TextBoxes programmatically within event handler methods - the CheckBox controls are also using DataBinding to boolean properties on the POCO.
When you setup databinding by this overload: Binding(String, Object, String), then the value of DataSourceUpdateMode will be OnValidation, which means when you modify the value of control's property using code or through UI, the binding will push the new value to data source only after Validating event happens for the control.
To fix the problem, use either of the following options:
Use another overload and set the DataSourceUpdateMode to OnProperetyChanged
OR, after setting the Value of the TextBox.Text call ValidateChildren method of the form.
Example - Set the DataSourceUpdateMode to OnProperetyChanged
public class Person
{
public string Name { get; set; }
public string LegalCode { get; set; }
public bool IsRealPerson { get; set; }
}
Person person;
private void Form1_Load(object sender, EventArgs e)
{
person = new Person() {
Name = "My Company", LegalCode = "1234567890", IsRealPerson = false };
NameTextBox.DataBindings.Add(nameof(TextBox.Text), person,
nameof(Person.Name), true, DataSourceUpdateMode.OnPropertyChanged);
LegalCodeTextBox.DataBindings.Add(nameof(TextBox.Text), person,
nameof(Person.LegalCode), true, DataSourceUpdateMode.OnPropertyChanged);
IsRealPersonCheckBox.DataBindings.Add(nameof(CheckBox.Checked), person,
nameof(Person.IsRealPerson), true, DataSourceUpdateMode.OnPropertyChanged);
IsRealPersonCheckBox.CheckedChanged += (obj, args) =>
{
if (IsRealPersonCheckBox.Checked)
{
LegalCodeTextBox.Text = null;
LegalCodeTextBox.Enabled = false;
}
};
}
Note - You can put the logic inside the model
Another solution (Which needs more effort and more changes in your code) is implementing INotifyPropertyChanged in your model class. Then when PropertyChanged event raises for your boolean property, you can check if it's false then you can set the string property to null.
In this approach you don't need to handle UI events. Also right after updating the model property, the UI will be updated; in fact implementing INotifyPropertyChanged enables two-way databinding for your model class.

Drop-down list in properties window to start at certain index

Background information:
We have a UserControl called Sensor.
Sensor has a property called SlaveSensor.
The type of the property SlaveSensor is Sensor.
public Sensor SlaveSensor;
{
get
{
return _slaveSensor;
}
set
{
//Some more code for checking various stuff...
_slaveSensor; = value;
}
}
As you can see, the type of the property is the same as the UserControl itself.
The property SlaveSensor is normally set via the properties window during design time.
Visual Studio automatically provides the editor as a drop-down list, from which one can select from all available Sensors on the form.
My question is:
How can I make the drop-down list start at a specified instance in the list,
to make it quicker to find the right Sensor to set for the property?
The name of the Sensor to set as the property is always nearly the same as the name of the Sensor for which the property is being set.
So if e.g. the drop-down list would simply auto scroll to the index in the list that has the name of the Sensor for which the property is being set,
I have achieved my goal.
What do I have so far:
I assume that I need to implement a custom property editor.
I might actually be able to create one with a drop-down list, and fill this with strings,
but the existing is OK as it is, I just need to tell it to drop-down to a certain index when clicked.
Thanks for any help in advance!
I'd try this.
string text = "SomeText";
var item = dropdown.Items.FindByText(text);
if(item!= null)
item.Selected = true;
Or by value:
string value = "SomeValue";
var item = dropdown.Items.FindByValue(value);
if (item != null)
item.Selected = true;
Taken from top answer here

Modify programmatically a RaiseAndSetIfChanged property (To move the Selected item)

Given this property that changes the Selected File every time I click an item I my listBox:
public IMediaFile SelectedPlayListFile
{
get { return this.selectedPlayListFile; }
set { this.RaiseAndSetIfChanged(ref selectedPlayListFile, value);
}
and called later with:
var file = this.SelectedPlayListFile;
How could i set the property without the RaiseAndSetIfChanged event? I need to change the SelectedPlayListFile programmatically and let the UI reflects that change too. (No more clicking)
So the SOLUTION was SO obvious...
this.SelectedPlayListFile = filesCollView.CurrentItem as IMediaFile;
Thank you for this newbie question.

Combobox does not change value when a different value is selected

I am trying to migrate a small prototype application I made in WinForms to WPF. I'm having some issues with a combobox in WPF not changing values when I select a different value from the drop-down. Initially, I tried just copying the code that I used in my WinForms app to populate the combobox and determine if a new index had been selected. This is how my WinForms code looked like:
private void cmbDeviceList_SelectedIndexChanged(object sender, EventArgs e)
{
var cmb = (Combobox) sender;
var selectedDevice = cmb.SelectedItem;
var count = cmbDeviceList.Items.Count;
// find all available capture devices and add to drop down
for(var i =0; i<count; i++)
{
if(_deviceList[i].FriendlyName == selectedDevice.ToString())
{
_captureCtrl.VideoDevices[i].Selected = true;
break;
}
}
}
Earlier in the code, I am populating the _deviceList List and the combo box (in Form1_Load to be specific) by looping over the available devices and adding them. I tried the same approach in WPF and could only populate the combo box. When I selected a new value, for some reason the same exact value (the initial device) was being sent into the event code (cmbCaptureDevices_SelectionChanged in my WPF app). I looked around for some tutorials in WPF and found that maybe data binding was my issue, and I tried that out instead. This is my combobox in my XAML file:
<ComboBox ItemsSource="{Binding Devices}" Name="cmbCaptureDevices"
IsSynchronizedWithCurrentItem="True" SelectedItem="{Binding CurrentDevice,
Mode=TwoWay}" Se;ectionChanged="cmbCapturedDevices_SelectionChanged" />
There's more to that XAML definition, but it's all arbitrary stuff like HorizontalAlignment and whatnot. My VideoDevicesViewModel inherits from INotifyPropertyChanged, has a private List<Device> _devices and a private Device _currentDevice. The constructor looks like:
public VideoDevicesViewModel()
{
_devices = GetCaptureDevices();
DevicesCollection = new CollectionView(_devices);
}
GetCaptureDevices simply is the loop that I had in my WinForms app which populates the list with all avaialble capture devices on the current machine. I have a public CollectionView DevicesCollection { get; private set; } for getting/setting the devices at the start of the application. The property for my current device looks like:
public Device CurrentDevice
{
get { return _currentDevice; }
set
{
if (_currentDevice = value)
{
return;
}
_currentDevice = value;
OnPropertyChanged("CurrentDevice");
}
}
OnPropertyChanged just raises the event PropertyChanged if the event isn't null. I'm new to WPF (and pretty new to C# in general, honestly) so I'm not sure if I'm missing something elementary or not. Any idea as to why this combobox won't change values for me?
Discovered the answer on my own here. The unexpected behavior was a result of using the Leadtools Device class. It's a COM component and apparently was not playing nicely with my application. I honestly don't understand why exactly it worked, but I wrapped the Device class in another class and used that instead. As soon as I was using the wrapper class, the combo box functioned as it should.
You are using the assignment operator '=' instead of the equality operator '=='
Change
if (_currentDevice = value)
to
if (_currentDevice == value)
Try the following
if _currentDevice == value ...

determine a textbox's previous value in its lost focused event? WPF

I have a textbox and have an onlostfocus event on it.
Inside the lostfocus method, is there a way I can determine if the user has actually changed the value in it?
i.e how do i get hold of any previous value in it?
Thanks
As with just about everything else in WPF, this is easier if you use data binding.
Bind the text box to a class property. By default, bindings update the source when the bound control loses focus, so you don't have to muck around with the LostFocus event. You then have access to both the new value and the value that the user entered in the property setter.
In the XAML it looks like this:
<TextBox Text="{Binding MyProperty, Mode=TwoWay}"/>
In the class it looks like this:
private string _MyProperty;
public string MyProperty
{
get { return _MyProperty; }
set
{
// at this point, value contains what the user just typed, and
// _MyProperty contains the property's previous value.
if (value != _MyProperty)
{
_MyProperty = value;
// assuming you've implemented INotifyPropertyChanged in the usual way...
OnPropertyChanged("MyProperty");
}
}
What comes to mind for me is a two stage approach. Handle the TextChanged event on the textbox and flag it. Then when the textbox OnLostFocus occurs you can simply check your flag to see if the text has been changed.
Here is a code snippet on how you could handle the tracking.
public class MyView
{
private bool _textChanged = false;
private String _oldValue = String.Empty;
TextChanged( ... )
{
// The user modifed the text, set our flag
_textChanged = true;
}
OnLostFocus( ... )
{
// Has the text changed?
if( _textChanged )
{
// Do work with _oldValue and the
// current value of the textbox
// Finished work save the new value as old
_oldValue = myTextBox.Text;
// Reset changed flag
_textChanged = false;
}
}
}
Store the original value somewhere. You could write a common component to store the value when it gets focus and compare the value when it loses focus. I've done this in ASP.NET and it works quite well.
Another way to solve this by databinding:
Bind the TextBox.Text to the property, that holds the inital value, but use a binding with
UpdateSourceTrigger=Explicit
Then, when the textbox loses focus, you can check the binding if source and target values differ, using this code snippet and evaluating the resulting BindingExpression:
BindingExpression be = tb.GetBindingExpression(TextBox.TextProperty);
Some more code can be found here:
http://bea.stollnitz.com/blog/?p=41

Categories