Editable Combobox not updating when empty - c#

I have an editable combobox here:
<ComboBox IsEditable="True"
Text="{Binding Model.TNumber}" SelectedItem="{Binding SelectedT}" ItemsSource="{Binding TList}"
IsEnabled="{Binding EnableTComboBox}"/>
The combobox works, except when you backspace it and have it empty.The box will default to the first value in the list. If I backspace it and leave the combobox empty, the variable TNumber will still contain the previously entered number.
Is there a way to have the variable to be empty?
TNumber is a short.

I'm not sure at which point you are checking the value of TNumber, but what you could do is set it's value in a SelectionChanged event handler:
private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
TNumber = (sender as ComboBox).SelectedItem as short?;
}
But this requires that you define TNumber as a nullable short (short?).
Alternatively, if you didn't want to allow a nullable short, you could use:
private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var newValue = (sender as ComboBox).SelectedItem;
if (newValue == null)
TNumber = 0;
else
TNumber = (short)newValue;
}
Which first checks whether the new value is equal to null. If it is, then we set TNumber to 0 (or whatever value you need it to be).
While testing for this, I noticed that TNumber seems to be updated after the SelectioinChanged event fires, meaning that whenever you check the value of TNumber inside the event, it will contain the previous value.
One way of working around this is by adding this line to the top of the event handler:
comboBox.Items.Refresh();
Which sets TNumber to the new value. However this does not work when deleting the value from the ComboBox! (In this case, TNumber is again only updated after the SelectionChanged event handler).
I have no idea why this is, but I also tested it when working with TNumber as a short? with the same result.
private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
comboBox.Items.Refresh();
var value = (sender as ComboBox).SelectedItem; // Tested using short? with same result
var x = TNumber;
}
Perhaps someone else could shed som light on why this happens, or at which point exactly the TNumber value is updated from the binding.

Related

Windows 10 uwp listview selection changed not working

Windows 10 uwp app with a listview to just list out strings. First of all, I have an observable collection of strings in my xaml code behind. Because I still dont understand proper data binding in xaml, I am currently adding the strings to tje listview by doing a foreach loop on the observable collection and then doing
Listview1.Items.Add (new TextBlock {Text = myString});
However, is binding in this case as easy as setting my listview ItemsSource to my observablecollection?
My main issue though is I want to know when a user selects a string in the listview and what string they selected. So, I wired up to the listview SelectionChanged event. This event will raise when I select an item in the list, however
var selectedString = e.AddedItems.First().ToString();
Does not give me the selected string value. Also, there seems to be a possible recursion issue with this event. At one point, my breakpoint hit twice even though I had only selected an item in the listview one time.
So, main question is how to i get the selected string from the listview but also would appreciate suggestions or comments on data binding and if there could be recursion with this event.
EDIT: After trying the last two answers, I am still having some issues here. I cannot get the string that is selected. Using both answers below, I get the same results. First, there is some recursion because clearly the event does fire twice most times even when the list is selected only one time. Also, in both cases, the string is never populated with the selection. In fact, the breakpoint will hit at the line but then skip to the end of the event handler method and I cannot inspect any of the variables or arguments. I even wrapped it up in a try catch block but it never runs the rest of the code in the try block and never catches an exception. All it does is skip to the end of the event handler method but then take me to a file called SharedStubs.g.cs and in there, it hits at the end of this method
// Signature, Windows.UI.Xaml.UnhandledExceptionEventHandler.Invoke, [rev] [return] [Mcg.CodeGen.ComHRESULTReturnMarshaller] void__int, [rev] [in] [Mcg.CodeGen.WinRTInspectableMarshaller] object____mcg_IInspectable, [rev] [in] [GenericTypeMarshaller] -> T,
[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
[global::System.Runtime.InteropServices.McgGeneratedMarshallingCode]
internal static int Proc_object__TArg0__<TArg0>(
object __this,
global::System.Runtime.InteropServices.__vtable_IInspectable* unsafe_sender,
void* unsafe_e,
global::System.IntPtr __methodPtr)
{
// Setup
object sender = default(object);
TArg0 TArg0__arg = default(TArg0);
try
{
// Marshalling
sender = global::System.Runtime.InteropServices.McgMarshal.IInspectableToObject(((global::System.IntPtr)unsafe_sender));
TArg0__arg = (TArg0)global::System.Runtime.InteropServices.McgModuleManager.ComInterfaceToObject(
((global::System.IntPtr)unsafe_e),
typeof(TArg0).TypeHandle
);
// Call to managed method
global::McgInterop.Intrinsics.HasThisCall__Proc_object__TArg0__<TArg0>(
__this,
__methodPtr,
sender,
TArg0__arg
);
global::System.Runtime.InteropServices.DebugAnnotations.PreviousCallContainsUserCode();
// Return
return global::McgInterop.Helpers.S_OK;
}
catch (global::System.Exception hrExcep)
{
// ExceptionReturn
return global::System.Runtime.InteropServices.McgMarshal.GetHRForExceptionWinRT(hrExcep);
}
}
And the sender in this method is ListView. After it hits in this method, the debugger just sort of hangs. I never get a real exception or error and it never really stops. I can hit continue but it just sits idle. So, the above is the only clue I really have. Not sure why this would hit but not the try/catch block and why I would never get any further exception, stack trace, etc...
Thanks!
Can you please try this one?
private void Listview1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
TextBlock textBlock = (sender as ListView).SelectedItem as TextBlock;
string value = textBlock.Text;
// OR
string value2 = (e.AddedItems[0] as TextBlock).Text;
// OR
string value3 = (e.AddedItems.First() as TextBlock).Text;
}
First of all, binding string items to a listview requires one line of code. You don't have to create a XAML template for that since you're not binding a object with properties. You can just do this:
Listview1.ItemsSource = YourObservableCollection();
It will bind your collection to your ListView.
As for the selection event, instead of SelectionChanged, you can use ItemClick event. The event args will give you the selected item aka the string by calling e.ClickedItem.
First, enable your ListView1's IsItemClickEnabled. Set it from false to true. Then add the ItemClick event.
private void ListView1_ItemClick(object sender, ItemClickEventArgs e)
{
e.ClickedItem;
}
This will return the value selected, in your case, the string.
Hope it helps!
You get the currently selected item in a ListView using the SelectedItem property and since you are adding TextBlock elements to the Items collection you should cast the SelectedItem property to a TextBlock and then access its Text property to get the string value:
private void Listview1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
TextBlock textBlock = Listview1.SelectedItem as TextBlock;
if (textBlock != null)
{
string s = textBlock.Text;
}
}
you can also use SelectionChanged event to get the value selected by user.
Here is how the code will look like :
In XAML :
ListView Name="sourceList"
ItemsSource="{Binding itemsource}"
SelectionChanged="sourceList_SelectionChanged"
In Code behind :
private void sourceList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string selectedSource = Convert.ToString(((ListView)sender).SelectedItem);
}

How to test which combobox has been most recently changed? WPF

Is there a way to check to see which combobox which value has been most recently changed?
how to check if item is selected from a comboBox in C#
I know you can do this,
if (MyComboBox.SelectedIndex >= 0)
{
//do stuff
}
The problem I am having is that I am combining Event Handlers in to one handler due to the amount of comboboxes and having one event for each combobox really is not practical if I can help it.
Is there a way to have a variable assigned giving you the name of the combobox which value has been most recently changed? Or will I have to use individual event handlers for each combobox?
Actually It will be very easy to track most recent combobox change when you using single event handler for all combobox. You can do by following way.
string lastComboName=""; // define global variable
//common event handler for all combobox
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var cmb = (ComboBox)sender;
lastComboName = cmb.Name;
}
Hope that each comboBox having an unique name, then we can use those name to identify which one is the sender of event: Now consider the following code for this:
private void CboFirst_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox selctedComboBox = sender as ComboBox;
string ComboName = selctedComboBox.Name;
// Do something
}
Now tracking the last updated ComboBox, You can achieve this by keeping a blobal variable and update it in every trigger so Every time it holds the Latest value( the name of the combobox)

Remove something added by a particular event - c#

I have a piece of code that will add the selected item of a combobox to a listbox when a checkbox is checked. I would like to remove that selected item to be removed from the listbox when the checkbox is unchecked.
My problem is I cant simply repeat the code for removal to be the same as add because the combobox selection will different or empty when unchecked.
This is how it currently looks:
private void CBwasher_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked == true)
{
listBox2.Items.Add(comboBox1.SelectedItem);
}
if (checkBox1.Checked == false)
{
listBox2.Items.Remove(comboBox1.SelectedItem);
}
So I need a way of removing whatever was added by that check change instead of removing the selected index of the combobox. Please consider that there may be multiple lines in the listbox added by multiple different checkboxes.
You simply need to store the added item and remove it.
private object addedItem;
private void CBwasher_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
addedItem = comboBox1.SelectedItem;
listBox2.Items.Add(addedItem);
}
else
{
listBox2.Items.Remove(addedItem);
}
}
You may also need to check SelectedItem is null before adding/removing the item.
Focusing on the part where you said there might be multiple different checkboxes,
you need to store one item per checkbox.
You can write your own child class of the checbox control to add this feature, or simply use the Tag property.
You can also indicate which checkbox is linked to which combobox in the same way. Either child class or use the Tag property.
In my example, I'll assume you've referenced the combobox from the checkbox using the Tag property.
You can do it manually like this
checkBox1.Tag = comboBox1;
or hopefully you can automate it if you are generating these on the fly.
Here is the general idea of how the checkbox event should look.
The event is is utilising the sender argument, which means you should hook up all your checkboxes CheckedChanged events to this one handler. No need to create separate handlers for each.
private void CBwasher_CheckedChanged(object sender, EventArgs e)
{
var checkBox = (CheckBox)sender;
var comboBox = (ComboBox)checkBox.Tag;
if (checkBox.Checked && comboBox.SelectedItem != null)
{
listBox2.Items.Add(comboBox.SelectedItem);
comboBox.Tag = comboBox.SelectedItem;
}
if (!checkBox.Checked && comboBox.Tag != null)
{
listBox2.Items.Remove(comboBox.Tag);
}
}

DataGridViewCheckBoxColumn get state

I've got a DataGridView dgView which I populate with several different Forms, e.g. a DataGridViewCheckBoxColumn. To handle events, I added
private void InitializeComponent()
{
...
this.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgView_CellClick);
...
}
The implementation looks like:
private void dgView_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (Columns[e.ColumnIndex].Name == "Name of CheckBoxColumn") // this is valid and returns true
{
Console.WriteLine("Handle single click!");
// How to get the state of the CheckBoxColumn now ??
}
}
This is where I am stuck. I already tried different approaches but with no success at all:
DataGridViewCheckBoxColumn cbCol = Rows[e.RowIndex].Cells[e.ColumnIndex] as DataGridViewCheckBoxColumn; // does not work
DataGridViewCheckBoxColumn cbCol = (DataGridViewCheckBoxColumn)sender; // nor this
if (bool.TryParse(Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString(), out isBool)) // nor this
{ ... }
Could anybody point out how to retrieve the State of this CheckBoxColumn please? Besides, do any other events exist do address the CheckBoxColumn directly? (such as "ValueChanged" or something)
Update:
The approach
DataGridViewCell dgvCell = Rows[e.RowIndex].Cells[e.ColumnIndex];
Console.WriteLine(dgvCell.Value);
at least returns true / false at the point of time before the value gets changed (or not) by clicking the cell. But all in all there should be a solution to address the CheckBoxColumn directly.
Solution:
Sometimes an answer is too obvious to see. The problem I was facing was that the event "CellClick" triggered when clicking on the cell as well as clicking on the checkbox. The proper handling therefore is to use a "CellValueChanged" event instead:
private void InitializeComponent()
{
...
this.CellValueChanged += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgView_CellValueChanged);
...
}
To determine the value of the checkbox I use the same way as stated above:
if (e.ColumnIndex != -1 && Columns[e.ColumnIndex].Name == "Name of Checkbox")
{
bool cbVal = Rows[e.RowIndex].Cells[e.ColumnIndex].Value;
}
One way to manipulate UI components is by use of Data Bindings. See for example:
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="Online Order?" IsThreeState="True" Binding="{Binding OnlineOrderFlag}" />
</DataGrid.Columns>
This links explains DataGrid usage in detail.
But anyways, have you tried doing QuickWatch the sender to see what is the type of it?
you could cast the cell to a checkbox and check if it's checked there...
CheckBox chkb = (CheckBox)Rows[e.RowIndex].FindControl("NameOfYourControl");
then just do a check against chkb
if (chkb.Checked == true)
{
//do stuff here...
}
You can simply cast the value of the cell to bool
//check if it's the good column
bool result = Rows[e.RowIndex].Cells[e.ColumnIndex].Value;
edit : I'm probably missing something in your question but if you update/add detail I will have a look
edit 2 : After the comment,
If you want to do it in the cellClick just reverse the value it give you.
bool result = !Rows[e.RowIndex].Cells[e.ColumnIndex].Value;
The proper handling is to use a "CellValueChanged" event:
private void InitializeComponent()
{
...
this.CellValueChanged += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgView_CellValueChanged);
...
}
To determine the value of the checkbox:
private void dgView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex != -1 && Columns[e.ColumnIndex].Name == "Name of Checkbox")
{
Console.WriteLine( Rows[e.RowIndex].Cells[e.ColumnIndex].Value );
}
}

ComboBox- SelectionChanged event has old value, not new value

C#, .NET 4.0, VS2010.
New to WPF. I have a ComboBox on my MainWindow. I hooked the SelectionChanged event of said combo box. However, if I examine the value of the combo box in the event handler, it has the old value. This sounds more like a "SelectionChanging" event, than a SelectionChanged event.
How do I get the new value of the ComboBox after the selection has actually happend?
Currently:
this.MyComboBox.SelectionChanged += new SelectionChangedEventHandler(OnMyComboBoxChanged);
...
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
string text = this.MyComboBox.Text;
}
Note, I get the same behaviour if I use the object being passed in the event args, e.g. e.OriginalSource.
According to MSDN, e.AddedItems:
Gets a list that contains the items that were selected.
So you could use:
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
string text = (e.AddedItems[0] as ComboBoxItem).Content as string;
}
You could also use SelectedItem if you use string values for the Items from the sender:
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
string text = (sender as ComboBox).SelectedItem as string;
}
or
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
string text = ((sender as ComboBox).SelectedItem as ComboBoxItem).Content as string;
}
Since both Content and SelectedItem are objects, a safer approach would be to use .ToString() instead of as string
The correct value to check here is the SelectedItem property.
A ComboBox is a composite control with two of its parts being:
The Text Part: the value in the this part corresponds to the Text property of the ComboBox.
The Selector Part (i.e. the "drop-down" part): The selected item in this part corresponds to the SelectedItem property.
The image above was taken immediately after the ComboBox was expanded (i.e. before selecting a new value). At this point both Text and SelectedItem are "Info", assuming the ComboBox items were strings. If the ComboBox items were instead all the values of an Enum called "LogLevel", SelectedItem would currently be LogLevel.Info.
When an item in the drop-down is clicked on, the value of SelectedItem is changed and the SelectionChanged event is raised. The Text property isn't updated yet, though, as the Text Part isn't updated until after the SelectionChanged handler is finished. This can be observed by putting a breakpoint in the handler and looking at the control:
Since the Text Part hasn't been updated at this point, the Text property returns the previously selected value.
Use the DropDownClosed event instead of selectionChanged if you want the current value of the combo box.
private void comboBox_DropDownClosed(object sender, EventArgs e)
{
MessageBox.Show(comboBox.Text)
}
Is really that simple.
This worked for me:
private void AppName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBoxItem cbi = (ComboBoxItem)AppName.SelectedItem;
string selectedText = cbi.Content.ToString();
}
This worked for me:
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
var text = ((sender as ComboBox).SelectedItem as ComboBoxItem).Content as string;
}
Following event is fired for any change of the text in the ComboBox (when the selected index is changed and when the text is changed by editing too).
<ComboBox IsEditable="True" TextBoxBase.TextChanged="cbx_TextChanged" />
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
string newItem = ((DataRowView) e.AddedItems[0]).Row.ItemArray[0].ToString();
}
The second option didn't work for me because the .Text element was out of scope (C# 4.0 VS2008). This was my solution...
string test = null;
foreach (ComboBoxItem item in e.AddedItems)
{
test = item.Content.ToString();
break;
}
If you really need the SelectionChanged event, then the best answer is SwDevMan81's answer. However, if you are starting with WPF, then you might want to learn how to do things the WPF way, which is different than the old Windows Forms days that used to rely on events like SelectionChanged, with WPF and Model View ViewModel pattern, you should use bindings. Here is a code example:
// In the Views folder: /Views/MyWindow.xaml:
// ...
<ComboBox ItemsSource="{Binding MyViewModel.MyProperties, RelativeSource={RelativeSource AncestorType=Window}}"
SelectedItem="{Binding MyViewModel.MyProperty , RelativeSource={RelativeSource AncestorType=Window}}" />
// ...
// In the Views folder: /Views/MyWindow.xaml.cs:
public partial class MyWindow : Window
{
public MyViewModelClass MyViewModel {
get { return _viewModel; }
private set { _viewModel = value;}
}
public MyWindow()
{
MyViewModel.PropertyChanged += MyViewModel_PropertyChanged;
}
void MyViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "MyProperty")
{
// Do Work
// Put your logic here!
}
}
}
using System.ComponentModel;
// In your ViewModel folder: /ViewModels/MyViewModelClass.cs:
public class MyViewModelClass : INotifyPropertyChanged
{
// INotifyPropertyChanged implementation:
private void NotifyPropertyChanged(string propertyName = "") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }
public event PropertyChangedEventHandler PropertyChanged;
// Selected option:
private string _myProperty;
public string MyProperty {
get { return _myProperty; }
set { _myProperty = value; NotifyPropertyChanged("MyProperty"); }
}
// Available options:
private List<string> _myProperties;
public List<string> MyProperties {
get { return _myProperties; }
set { _myProperties = value; NotifyPropertyChanged("MyProperties"); }
}
}
private void indBoxProject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int NewProjID = (e.AddedItems[0] as kProject).ProjectID;
this.MyProject = new kProject(NewProjID);
LoadWorkPhase();
}
The use of the e.AddedItems[0] as kProject where kProject is a class that holds the data worked for me as it was defaulting to the RemovedItems[0] before I have made this explicit distinction. Thanks SwDevMan81 for the initial information that answered this question for me.
I needed to solve this in VB.NET. Here's what I've got that seems to work:
Private Sub ComboBox1_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs) Handles ComboBox_AllSites.SelectionChanged
Dim cr As System.Windows.Controls.ComboBoxItem = ComboBox1.SelectedValue
Dim currentText = cr.Content
MessageBox.Show(currentText)
End Sub
It's weird that SelectedItem holds the fresh data, whereas SelectedValue doesn't. Sounds like a bug to me. If your items in the Combobox are objects other than ComboBoxItems, you will need something like this: (my ComboBox contains KeyValuePairs)
var selectedItem = (KeyValuePair<string, string>?)(sender as ComboBox).SelectedItem;
if (!selectedItem.HasValue)
return;
string selectedValue = selectedItem.Value.Value; // first .Value gets ref to KVPair
ComboBox.SelectedItem can be null, whereas Visual Studio keeps telling me that a KeyValuePair can't be null. That's why I cast the SelectedItem to a nullable KeyValuePair<string, string>?. Then I check if selectedItem has a value other than null. This approach should be applicable to whatever type your selected item actually is.
Don't complicate things for no reason. Using SelectedValue property you can easily get a selected ComboBox value like this: YourComboBoxName.SelectedValue.ToString().
Behind the scene the SelectedValue property is defined as: SelectedValue{get; set;} this means you can use it to get or set the value of a ComboBox.
Using SelectedItem is not an efficient way to get a ComboBox value since it requires a lot of ramifications.
You can check SelectedIndex or SelectedValue or SelectedItem property in the SelectionChanged event of the Combobox control.
From SelectionChanged event of a combobox you can get the selected item text as follow:
private void myComboBox_SelectionChanged (object sender, SelectionChangedEventArgs e)
{
ComboBoxItem comboBoxItem = (ComboBoxItem) e.AddedItems[0];
string selectedItemText = comboBoxItem.Content.ToString();
}
clean and easy - TextChanged
private void ComboBox1_TextChanged(object sender, EventArgs e)
{
MessageBox.Show(ComboBox1.Text);
}
Following works for me to get current selection of ComboBox (c# / .net6):
private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
string? text = ((ComboBoxItem)(sender as ComboBox).SelectedItem).Content as string;
if(text != null){
...
}
}
This should work for you ...
int myInt= ((data)(((object[])(e.AddedItems))[0])).kid;
I solved this by using the DropDownClosed event because this fires slightly after the value is changed.

Categories